From 72ce76812eace530f2d5bfc8d24cef89769be063 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 18 Nov 2025 03:50:06 -0800 Subject: [PATCH] WIP --- .../src/hdl_bundle.rs | 66 +- .../fayalite-proc-macros-impl/src/hdl_enum.rs | 16 +- crates/fayalite-proc-macros-impl/src/lib.rs | 29 +- crates/fayalite/examples/blinky.rs | 2 +- crates/fayalite/examples/tx_only_uart.rs | 2 +- crates/fayalite/src/array.rs | 16 +- crates/fayalite/src/bundle.rs | 82 +- crates/fayalite/src/clock.rs | 40 +- crates/fayalite/src/enum_.rs | 48 +- crates/fayalite/src/expr.rs | 439 +++- crates/fayalite/src/expr/ops.rs | 2287 ++++++++++++++--- .../fayalite/src/expr/ops/test_ops_impls.rs | 218 ++ crates/fayalite/src/expr/target.rs | 13 +- crates/fayalite/src/expr/value_category.rs | 377 +++ crates/fayalite/src/firrtl.rs | 18 +- crates/fayalite/src/int.rs | 720 +++++- crates/fayalite/src/memory.rs | 19 +- crates/fayalite/src/module.rs | 43 +- .../src/module/transform/deduce_resets.rs | 9 +- .../src/module/transform/simplify_enums.rs | 27 +- .../src/module/transform/simplify_memories.rs | 4 +- crates/fayalite/src/module/transform/visit.rs | 2 +- crates/fayalite/src/phantom_const.rs | 20 +- crates/fayalite/src/platform.rs | 10 +- crates/fayalite/src/prelude.rs | 2 +- crates/fayalite/src/reg.rs | 18 +- crates/fayalite/src/reset.rs | 28 +- crates/fayalite/src/sim.rs | 15 +- crates/fayalite/src/sim/compiler.rs | 107 +- crates/fayalite/src/sim/interpreter.rs | 1 + crates/fayalite/src/sim/value.rs | 272 +- .../src/sim/value/sim_only_value_unsafe.rs | 11 +- crates/fayalite/src/ty.rs | 2 +- crates/fayalite/src/util/ready_valid.rs | 4 +- crates/fayalite/src/wire.rs | 16 +- crates/fayalite/tests/module.rs | 2 +- crates/fayalite/tests/sim.rs | 2 +- 37 files changed, 4145 insertions(+), 842 deletions(-) create mode 100644 crates/fayalite/src/expr/ops/test_ops_impls.rs create mode 100644 crates/fayalite/src/expr/value_category.rs diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f952f426..3a3d3cee 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) } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 885cf871..90838f0b 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 13ec7a2e..152053cb 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 75799fdc..d2cdb336 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 5c20b395..59e29682 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 569f2e28..329334fc 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -3,7 +3,7 @@ use crate::{ expr::{ - CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, + CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, ValueType, ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq}, }, int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, @@ -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()) }); @@ -331,9 +331,7 @@ where Lhs: ExprPartialEq, { 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()); + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_eq(r)) @@ -343,9 +341,7 @@ where } 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()); + assert_eq!(lhs.ty().len(), rhs.ty().len()); lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_ne(r)) @@ -374,7 +370,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 0edf1928..5fb52487 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -3,8 +3,9 @@ use crate::{ expr::{ - CastToBits, Expr, ReduceBits, ToExpr, + CastToBits, Expr, ReduceBits, ToExpr, ToSimValueInner, ValueType, ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue}, }, int::{Bool, DynSize}, intern::{Intern, InternSlice, Interned}, @@ -317,7 +318,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 +354,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 +567,54 @@ macro_rules! impl_tuples { builder.finish() }; } - impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { + impl<$($T: ToSimValue,)*> ToSimValueInner for ($($T,)*) + where + Self: ValueType, + { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + let ($($var,)*) = this; + ($($var.to_sim_value(),)*) + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + let ($($var,)*) = this; + ($($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 +650,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,19 +672,21 @@ 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,)*) { @@ -775,9 +809,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 +844,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 909edbdf..168142b7 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 083072b5..e1a6dfa3 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -3,7 +3,7 @@ use crate::{ expr::{ - Expr, ToExpr, + Expr, ToExpr, ValueType, ops::{ExprPartialEq, VariantAccess}, }, hdl, @@ -599,7 +599,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 +711,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) } } @@ -813,7 +813,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 +853,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 +861,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 +879,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 +894,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 +969,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 +994,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 +1009,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 +1021,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 +1033,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 +1045,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 +1057,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 +1075,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 +1093,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 +1107,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 89e60cda..ca793b6f 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -9,7 +9,7 @@ use crate::{ ops::ExprCastTo, target::{GetTarget, Target}, }, - int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, + int::{Bool, DynSize, IntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, module::{ @@ -17,9 +17,11 @@ use crate::{ transform::visit::{Fold, Folder, Visit, Visitor}, }, phantom_const::PhantomConst, + prelude::SimValue, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, ty::{CanonicalType, StaticType, Type, TypeWithDeref}, + util::ConstBool, wire::Wire, }; use bitvec::slice::BitSlice; @@ -27,6 +29,7 @@ use std::{convert::Infallible, fmt, ops::Deref}; pub mod ops; pub mod target; +pub mod value_category; macro_rules! expr_enum { ( @@ -77,9 +80,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 +294,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 +308,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 +331,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 +341,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: this.__ty.as_dyn_int(), + __ty: this.ty().as_dyn_int(), __flow: this.__flow, } } @@ -342,7 +351,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Bundle::new(this.__ty.fields()), + __ty: Bundle::new(this.ty().fields()), __flow: this.__flow, } } @@ -352,7 +361,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 +378,7 @@ impl Expr { { Expr { __enum: this.__enum, - __ty: Enum::new(this.__ty.variants()), + __ty: Enum::new(this.ty().variants()), __flow: this.__flow, } } @@ -379,7 +388,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 +418,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 +444,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 +489,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 +498,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 +538,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 +560,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 +582,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 +604,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 +643,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(), @@ -674,20 +665,21 @@ impl GetTarget for MemPort { } pub trait HdlPartialEq { - fn cmp_eq(self, rhs: Rhs) -> Expr; - fn cmp_ne(self, rhs: Rhs) -> Expr; + type Output: ValueType; + fn cmp_eq(&self, rhs: Rhs) -> Self::Output; + fn cmp_ne(&self, rhs: Rhs) -> Self::Output; } 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; + fn cmp_lt(&self, rhs: Rhs) -> Self::Output; + fn cmp_le(&self, rhs: Rhs) -> Self::Output; + fn cmp_gt(&self, rhs: Rhs) -> Self::Output; + fn cmp_ge(&self, rhs: Rhs) -> Self::Output; } pub trait ReduceBits { - type UIntOutput; - type BoolOutput; + type UIntOutput: ValueType>; + type BoolOutput: ValueType; fn reduce_bitand(self) -> Self::UIntOutput; fn reduce_bitor(self) -> Self::UIntOutput; fn reduce_bitxor(self) -> Self::UIntOutput; @@ -699,43 +691,109 @@ pub trait ReduceBits { fn parity_even(self) -> Self::BoolOutput; } -pub trait CastToBits { - fn cast_to_bits(&self) -> Expr; +pub trait CastToBits: ValueType { + type Output: ValueType; + fn cast_to_bits(&self) -> Self::Output; } -impl CastToBits for T { - fn cast_to_bits(&self) -> Expr { +impl CastToBits for T { + type Output = Expr; + fn cast_to_bits(&self) -> Self::Output { ops::CastToBits::new(Expr::canonical(self.to_expr())).to_expr() } } -pub trait CastBitsTo { +pub struct CastToBitsValueCategoryDispatch<'a, T: ?Sized + ToValueWithCategory>(&'a T); + +impl value_category::ValueCategoryDispatch + for CastToBitsValueCategoryDispatch<'_, T> +{ + type Type = T::Type; + type InputValueCategory = T::ValueCategory; + type Value = UIntValue; + type SimValue = SimValue; + type Expr = Expr; + type Valueless = Valueless; + + fn dispatch_value(self) -> Self::Value + where + Self: value_category::ValueCategoryDispatch< + InputValueCategory = value_category::ValueCategoryValue, + >, + { + let value = self.0.to_value_with_category(); + todo!() + } + + fn dispatch_sim_value(self) -> Self::SimValue + where + Self: value_category::ValueCategoryDispatch< + InputValueCategory = value_category::ValueCategorySimValue, + >, + { + todo!() + } + + fn dispatch_expr(self) -> Self::Expr + where + Self: value_category::ValueCategoryDispatch< + InputValueCategory = value_category::ValueCategoryExpr, + >, + { + self.0.to_expr() + } + + fn dispatch_valueless(self) -> Self::Valueless + where + Self: value_category::ValueCategoryDispatch< + InputValueCategory = value_category::ValueCategoryValueless, + >, + { + todo!() + } +} + +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 { +impl CastBitsTo for Expr> { + type Output = Expr; fn cast_bits_to(&self, ty: ToType) -> Expr { - ops::CastBitsTo::new(Expr::as_dyn_int(self.to_expr()), ty).to_expr() + ops::CastBitsTo::new(Expr::as_dyn_int(*self), ty).to_expr() } } -pub trait CastTo: ToExpr { - fn cast_to(&self, to_type: ToType) -> Expr - where - Self::Type: ExprCastTo, - { - ExprCastTo::cast_to(self.to_expr(), to_type) - } - fn cast_to_static(&self) -> Expr - where - Self::Type: ExprCastTo, - { - ExprCastTo::cast_to(self.to_expr(), ToType::TYPE) +pub trait CastToImpl { + type Output: ValueType; + fn cast_to(this: &Self, to_type: ToType) -> Self::Output; +} + +impl, ToType: Type> CastToImpl for Expr { + type Output = Expr; + fn cast_to(this: &Self, to_type: ToType) -> Self::Output { + ExprCastTo::cast_to(*this, to_type) } } -impl CastTo for T {} +pub trait CastTo: ValueType { + fn cast_to(&self, to_type: ToType) -> >::Output + where + Self: CastToImpl, + { + CastToImpl::cast_to(self, to_type) + } + fn cast_to_static(&self) -> >::Output + where + Self: CastToImpl, + { + CastToImpl::cast_to(self, ToType::TYPE) + } +} + +impl CastTo for T {} #[doc(hidden)] pub fn check_match_expr( @@ -761,7 +819,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 +827,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 +857,231 @@ 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 + } +} + +trait ToValueWithCategorySealed {} + +#[expect(private_bounds)] +pub trait ToValueWithCategory: ValueType + ToValueWithCategorySealed { + type ToValueWithCategoryOutput: ValueType; + fn to_value_with_category(this: &Self) -> Self::ToValueWithCategoryOutput; + fn into_value_with_category(this: Self) -> Self::ToValueWithCategoryOutput + where + Self: Sized; +} + +pub trait ToValueWithCategoryHelper: + ValueType +{ + type HelperOutput: ValueType; + fn to_value_with_category_helper(this: &Self) -> Self::HelperOutput; + fn into_value_with_category_helper(this: Self) -> Self::HelperOutput + where + Self: Sized; +} + +impl< + T: ?Sized + ToValueWithCategoryHelper + ValueType, + C: value_category::ValueCategory, +> ToValueWithCategorySealed for T +{ +} + +impl< + T: ?Sized + ToValueWithCategoryHelper + ValueType, + C: value_category::ValueCategory, +> ToValueWithCategory for T +{ + type ToValueWithCategoryOutput = T::HelperOutput; + fn to_value_with_category(this: &Self) -> Self::ToValueWithCategoryOutput { + Self::to_value_with_category_helper(this) + } + fn into_value_with_category(this: Self) -> Self::ToValueWithCategoryOutput + where + Self: Sized, + { + Self::into_value_with_category_helper(this) + } +} + +impl> + ToValueWithCategoryHelper for T +{ + type HelperOutput = Expr; + fn to_value_with_category_helper(this: &Self) -> Self::HelperOutput { + this.to_expr() + } + fn into_value_with_category_helper(this: Self) -> Self::HelperOutput + where + Self: Sized, + { + this.to_expr() + } +} + +impl< + T: ?Sized + crate::sim::value::ToSimValue, +> ToValueWithCategoryHelper for T +{ + type HelperOutput = crate::sim::value::SimValue; + fn to_value_with_category_helper(this: &Self) -> Self::HelperOutput { + this.to_sim_value() + } + fn into_value_with_category_helper(this: Self) -> Self::HelperOutput + where + Self: Sized, + { + this.into_sim_value() + } +} + +impl> + ToValueWithCategoryHelper for T +{ + type HelperOutput = Valueless; + fn to_value_with_category_helper(this: &Self) -> Self::HelperOutput { + this.to_valueless() + } + fn into_value_with_category_helper(this: Self) -> Self::HelperOutput + where + Self: Sized, + { + this.to_valueless() + } +} + +impl< + T: ?Sized + + ToSimValueInner< + ValueCategory = value_category::ValueCategoryValue, + Type: Type, + >, + O: ValueType, +> ToValueWithCategoryHelper for T +{ + type HelperOutput = O; + fn to_value_with_category_helper(this: &Self) -> Self::HelperOutput { + T::to_sim_value_inner(this) + } + fn into_value_with_category_helper(this: Self) -> Self::HelperOutput + where + Self: Sized, + { + T::into_sim_value_inner(this) + } +} + +pub trait ToSimValueInner: ValueType { + fn to_sim_value_inner(this: &Self) -> ::SimValue; + fn into_sim_value_inner(this: Self) -> ::SimValue + where + Self: Sized; +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 4b7902e7..3293cabd 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -8,11 +8,13 @@ use crate::{ enum_::{Enum, EnumType, EnumVariant}, expr::{ CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq, - HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, + HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, 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, }; @@ -33,29 +36,1569 @@ use num_traits::{ToPrimitive, Zero}; use std::{ 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_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:ty, + [$($RLifetimes:tt)*][$($RBounds:tt)*] $R:ty, + $(#[$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:ty, + $RLifetimes:tt $RBounds:tt $R:ty, + $(#[$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)*] $T:ty,) => { + impl<$($Lifetimes)* $($Bounds)*> ValueIntoBigInt for $T { + fn value_to_bigint(&self) -> BigInt { + (*self).into() + } + } + }; +} + +make_impls! { + #[kinds((sint_at_most_sim_value<'a, Width>), (uint_at_most_sim_value<'a, Width>))] + impl_value_to_bigint! {} +} + +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<_> { + self.into_sim_value_inner().bitand(rhs.into_sim_value_inner()).into_sim_value() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + self.into_sim_value_inner().bitor(rhs.into_sim_value_inner()).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + self.into_sim_value_inner().bitxor(rhs.into_sim_value_inner()).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((bool_sim_value<'l>))] + #[kinds(bool)] + impl_simple_binary_op_trait! { + BitAnd::bitand(self, rhs) -> SimValue<_> { + self.into_sim_value_inner().bitand(rhs).into_sim_value() + }, + BitOr::bitor(self, rhs) -> Expr<_> { + self.into_sim_value_inner().bitor(rhs).into_sim_value() + }, + BitXor::bitxor(self, rhs) -> Expr<_> { + self.into_sim_value_inner().bitxor(rhs).into_sim_value() + }, + } +} + +macro_rules! impl_shift_binary_op_trait { + ( + [$($LLifetimes:tt)*][$($LBounds:tt)*] $L:ty, + [$($RLifetimes:tt)*][$($RBounds:tt)*] $R:ty, + $(#[$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:ty, + $RLifetimes:tt $RBounds:tt $R:ty, + $(#[$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>))] + #[kinds(usize)] + impl_shift_binary_op_trait! { + 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>))] + #[kinds(usize)] + impl_shift_binary_op_trait! { + 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()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(Expr::as_dyn_int(self.to_expr()), 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()), rhs).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(Expr::as_dyn_int(self.to_expr()), 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), rhs.to_expr()).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrU::new(Expr::as_dyn_int(self), 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), rhs.to_expr()).to_expr() + }, + Shr::shr(self, rhs) -> Expr<_> { + DynShrS::new(Expr::as_dyn_int(self), 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().shl(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().shl(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().shl(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().shl(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(usize)] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs).into_sim_value() + }, + } +} + +make_impls! { + #[kinds((sint_sim_value<'l, L>))] + #[kinds(usize)] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs).into_sim_value() + }, + Shr::shr(self, rhs) -> SimValue<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).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().shl(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().shl(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>))] + #[kinds(usize)] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() >> rhs) + }, + } +} + +make_impls! { + #[kinds((sint_value<'l, L>))] + #[kinds(usize)] + impl_shift_binary_op_trait! { + Shl::shl(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).ty(); + ty.from_int_wrapping(self.value_to_bigint() << rhs) + }, + Shr::shr(self, rhs) -> SimValueInner<_> { + let ty = self.to_valueless().shl(rhs.to_valueless()).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 { @@ -107,9 +1650,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 +1667,7 @@ impl ToExpr for NotU { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.ty(), __flow: Flow::Source, } } @@ -138,12 +1688,6 @@ impl_unary_op_trait! { } } -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 +1708,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 +1725,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, } } @@ -195,12 +1746,6 @@ impl_unary_op_trait! { } } -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 +1766,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 +1783,7 @@ impl ToExpr for NotB { literal_bits: self.literal_bits, }) .intern(), - __ty: self.arg.__ty, + __ty: self.arg.ty(), __flow: Flow::Source, } } @@ -270,22 +1822,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(), @@ -310,57 +1861,6 @@ impl_unary_op_trait! { } } -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 +1912,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 +1930,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 +1957,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 +1967,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 +1985,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 +2025,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 +2035,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 +2053,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 +2088,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 +2098,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 +2116,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 +2147,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 +2157,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 +2181,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 +2206,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 +2216,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 +2235,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 +2266,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 +2276,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 +2294,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 +2320,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 +2340,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 +2353,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 +2398,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 +2427,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 +2440,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 +2468,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 +2493,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 +2512,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 +2548,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 +2588,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 +2607,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 +2661,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 +2671,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 +2691,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() - } - } }; } @@ -1282,10 +2725,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 +2735,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 +2755,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, $value:ident, $Trait:ident::$method:ident) => { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct $name { lhs: Expr<$ty>, @@ -1361,11 +2796,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 +2806,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,45 +2824,13 @@ 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 -} +binary_op_fixed_shift!(FixedShlU, UIntType, UIntValue, Shl::shl); +binary_op_fixed_shift!(FixedShlS, SIntType, SIntValue, Shl::shl); +binary_op_fixed_shift!(FixedShrU, UIntType, UIntValue, Shr::shr); +binary_op_fixed_shift!(FixedShrS, SIntType, SIntValue, Shr::shr); pub trait ExprPartialEq: Type { fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr; @@ -1439,33 +2844,34 @@ pub trait ExprPartialOrd: ExprPartialEq { fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr; } -impl HdlPartialEq for Lhs +impl> HdlPartialEq for Expr where - Lhs::Type: ExprPartialEq, + Lhs: ExprPartialEq, { - fn cmp_eq(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr()) + type Output = Expr; + fn cmp_eq(&self, rhs: R) -> Self::Output { + ExprPartialEq::cmp_eq(*self, rhs.to_expr()) } - fn cmp_ne(self, rhs: Rhs) -> Expr { - ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr()) + fn cmp_ne(&self, rhs: R) -> Self::Output { + ExprPartialEq::cmp_ne(*self, rhs.to_expr()) } } -impl HdlPartialOrd for Lhs +impl> HdlPartialOrd for Expr where - Lhs::Type: ExprPartialOrd, + Lhs: ExprPartialOrd, { - fn cmp_lt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr()) + fn cmp_lt(&self, rhs: R) -> Self::Output { + ExprPartialOrd::cmp_lt(*self, rhs.to_expr()) } - fn cmp_le(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr()) + fn cmp_le(&self, rhs: R) -> Self::Output { + ExprPartialOrd::cmp_le(*self, rhs.to_expr()) } - fn cmp_gt(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr()) + fn cmp_gt(&self, rhs: R) -> Self::Output { + ExprPartialOrd::cmp_gt(*self, rhs.to_expr()) } - fn cmp_ge(self, rhs: Rhs) -> Expr { - ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr()) + fn cmp_ge(&self, rhs: R) -> Self::Output { + ExprPartialOrd::cmp_ge(*self, rhs.to_expr()) } } @@ -1515,9 +2921,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(), @@ -1633,9 +3046,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 +3056,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 { @@ -1681,13 +3098,17 @@ macro_rules! impl_cast_bit_op { impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to); 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() } })? @@ -1738,9 +3159,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(), @@ -1793,7 +3221,9 @@ impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt); impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[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; @@ -1934,10 +3364,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 +3386,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 +3421,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 +3460,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 +3484,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 +3515,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 +3536,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 +3554,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 +3602,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 +3653,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 +3705,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 +3813,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 +3842,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 +3940,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(), @@ -2629,13 +4119,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 +4150,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 +4160,6 @@ impl CastBitsTo { pub fn arg(self) -> Expr { self.arg } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for CastBitsTo { @@ -2676,9 +4170,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 +4204,6 @@ impl Uninit { pub fn new(ty: T) -> Self { Self { ty } } - pub fn ty(self) -> T { - self.ty - } } impl ToLiteralBits for Uninit { @@ -2716,9 +4214,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 00000000..5c3aa654 --- /dev/null +++ b/crates/fayalite/src/expr/ops/test_ops_impls.rs @@ -0,0 +1,218 @@ +// 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, + > { + 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:ty, [$($RLifetimes:tt)*][$($RBounds:tt)*] $R:ty, !$Trait:ident::$f:ident) => { + 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:ty, [$($RLifetimes:tt)*][$($RBounds:tt)*] $R:ty, $Trait:ident::$f:ident) => { + const _: () = { + 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:ty, [$($RLifetimes:tt)*][$($RBounds:tt)*] usize, Shl::shl) => { + const _: () = { + 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:ty, [$($RLifetimes:tt)*][$($RBounds:tt)*] usize, Shr::shr) => { + const _: () = { + 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:ty, $RLifetimes:tt $RBounds:tt $R:ty, !$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:ty, $RLifetimes:tt $RBounds:tt $R:ty, $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 c8c55e93..95d8e0f5 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 00000000..80630b4f --- /dev/null +++ b/crates/fayalite/src/expr/value_category.rs @@ -0,0 +1,377 @@ +// 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 {} + +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 59fbec7f..aa91468b 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, 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})")) @@ -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 7fa77ce4..7938e598 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, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ToExpr, ToLiteralBits, ToSimValueInner, + ToValueless, ValueType, Valueless, target::{GetTarget, Target}, + value_category::ValueCategoryValue, }, hdl, intern::{Intern, Interned, Memoize}, @@ -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,92 @@ 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 ToSimValueInner for $value { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + this.clone() + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + this + } + } + + impl ToSimValueInner for &'_ $value { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + this.clone() + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + this.clone() + } + } + + impl<'r, L: Size, R: Size> HdlPartialEq<&'r $value> for $value { + type Output = bool; + fn cmp_eq(&self, rhs: &'r $value) -> Self::Output { + if self.width() == rhs.width() { + self.bits() == rhs.bits() + } else { + self.to_bigint() == rhs.to_bigint() + } + } + fn cmp_ne(&self, rhs: &'r $value) -> Self::Output { + !self.cmp_eq(rhs) + } + } + + impl<'r, L: Size, R: Size> HdlPartialOrd<&'r $value> for $value { + fn cmp_lt(&self, rhs: &'r $value) -> Self::Output { + self.hdl_cmp(rhs).is_lt() + } + fn cmp_le(&self, rhs: &'r $value) -> Self::Output { + self.hdl_cmp(rhs).is_le() + } + fn cmp_gt(&self, rhs: &'r $value) -> Self::Output { + self.hdl_cmp(rhs).is_gt() + } + fn cmp_ge(&self, rhs: &'r $value) -> Self::Output { + self.hdl_cmp(rhs).is_ge() + } + } + + impl HdlPartialEq<$value> for $value { + type Output = bool; + fn cmp_eq(&self, rhs: $value) -> Self::Output { + self.cmp_eq(&rhs) + } + fn cmp_ne(&self, rhs: $value) -> Self::Output { + self.cmp_ne(&rhs) + } + } + + impl HdlPartialOrd<$value> for $value { + fn cmp_lt(&self, rhs: $value) -> Self::Output { + self.cmp_lt(&rhs) + } + fn cmp_le(&self, rhs: $value) -> Self::Output { + self.cmp_le(&rhs) + } + fn cmp_gt(&self, rhs: $value) -> Self::Output { + self.cmp_gt(&rhs) + } + fn cmp_ge(&self, rhs: $value) -> Self::Output { + self.cmp_ge(&rhs) + } } impl ToLiteralBits for $value { @@ -923,6 +1229,17 @@ macro_rules! impl_int { Arc::make_mut(&mut self.bits) } } + + /*impl<'l, 'r, L: Size, R: Size> std::ops::Add<&'r $value> for &'l $value { + type Output = $value; + + fn add(self, rhs: &'r $value) -> Self::Output { + self.to_valueless() + .add(rhs.to_valueless()) + .ty() + .value_from_bigint_wrapping(&(self.into_sim_value_inner().to_bigint() + BigInt::from(rhs))) + } + }*/ }; } @@ -1015,11 +1332,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 +1392,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 ToSimValueInner for $prim_int { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + 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 +1426,25 @@ macro_rules! impl_prim_int { } } $(#[$meta])* - impl ToExpr for NonZero<$prim_int> { + impl ToSimValueInner for NonZero<$prim_int> { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + Self::into_sim_value_inner(*this) + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + 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,25 +1452,203 @@ 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!() +); + +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 + sealed::BoolOrIntTypeSealed { @@ -1307,6 +1882,47 @@ impl ToLiteralBits for bool { } } +impl ToSimValueInner for bool { + fn to_sim_value_inner(this: &Self) -> ::SimValue { + *this + } + fn into_sim_value_inner(this: Self) -> ::SimValue { + 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/memory.rs b/crates/fayalite/src/memory.rs index 46eb59bd..83e74376 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 5ffeaae2..9d1a0e76 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 bd4e939c..61167fda 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 bd5f7d52..8902921d 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 35f186d3..d7418361 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 2c33a76b..2869a49a 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 eb6a7584..70920f74 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -3,7 +3,7 @@ use crate::{ expr::{ - Expr, ToExpr, + Expr, ToExpr, ValueType, ops::{ExprPartialEq, ExprPartialOrd}, }, int::Bool, @@ -374,48 +374,46 @@ 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)); + assert_eq!(lhs.ty(), rhs.ty()); true.to_expr() } fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + assert_eq!(lhs.ty(), rhs.ty()); false.to_expr() } } impl ExprPartialOrd for PhantomConst { fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + assert_eq!(lhs.ty(), rhs.ty()); false.to_expr() } fn cmp_le(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + assert_eq!(lhs.ty(), rhs.ty()); true.to_expr() } fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + assert_eq!(lhs.ty(), rhs.ty()); false.to_expr() } fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr { - assert_eq!(Expr::ty(lhs), Expr::ty(rhs)); + assert_eq!(lhs.ty(), rhs.ty()); true.to_expr() } } impl SimValuePartialEq for PhantomConst { fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - assert_eq!(SimValue::ty(this), SimValue::ty(other)); + assert_eq!(this.ty(), other.ty()); true } } impl ToSimValue for PhantomConst { - type Type = PhantomConst; - fn to_sim_value(&self) -> SimValue { SimValue::from_value(*self, *self) } @@ -479,7 +477,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 194aa6e3..c6901cf1 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 4cc173e8..4c5bfdf4 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 20e0b944..7f506552 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 5dff2784..d9b7e8ac 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -2,8 +2,9 @@ // See Notices.txt for copyright information use crate::{ clock::Clock, - expr::{Expr, ToExpr, ops}, + expr::{Expr, ValueType, ops}, int::{Bool, SInt, UInt}, + prelude::SimValue, source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, @@ -132,29 +133,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 +177,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(SyncReset, *self) } } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 808ead40..bf24befb 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1504,9 +1504,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 +2890,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 +2923,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 +3007,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, @@ -3272,7 +3273,7 @@ macro_rules! impl_simulation_methods { value: impl ToExpr, ) { let value = value.to_expr(); - assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + assert_eq!(io.ty(), value.ty(), "type mismatch"); let value = value .to_literal_bits() .expect("the value that is being written to an input must be a literal"); @@ -3343,7 +3344,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 7a0ac0a2..07621c5c 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 391172e0..2b121b52 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 ff836d52..112ef1ca 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, ToExpr, ToValueless, ValueType, Valueless, + 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(); @@ -450,48 +458,160 @@ impl, U: Type> PartialEq> for SimValue { } } -impl SimValuePartialEq for UIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueEq: SimValuePartialEq {} + +impl Eq for SimValue {} + +pub trait SimValuePartialOrd: SimValuePartialEq { + #[track_caller] + fn sim_value_partial_cmp( + this: &SimValue, + other: &SimValue, + ) -> Option; +} + +impl, U: Type> PartialOrd> for SimValue { + #[track_caller] + fn partial_cmp(&self, other: &SimValue) -> Option { + T::sim_value_partial_cmp(self, other) } } -impl SimValuePartialEq for SIntType { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +pub trait SimValueOrd: SimValuePartialOrd + SimValueEq { + #[track_caller] + fn sim_value_cmp(this: &SimValue, other: &SimValue) -> std::cmp::Ordering; +} + +impl Ord for SimValue { + #[track_caller] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + T::sim_value_cmp(self, other) } } -impl SimValuePartialEq for Bool { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValuePartialEq> for UIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + this.hdl_eq(&other) } } -impl SimValuePartialEq for Clock { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValueEq for UIntType {} + +impl SimValuePartialOrd> for UIntType { + fn sim_value_partial_cmp( + this: &SimValue, + other: &SimValue>, + ) -> Option { + Some(this.hdl_cmp(other)) } } -impl SimValuePartialEq for Reset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValueOrd for UIntType { + fn sim_value_cmp(this: &SimValue, other: &SimValue) -> std::cmp::Ordering { + this.hdl_cmp(other) } } -impl SimValuePartialEq for SyncReset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValuePartialEq> for SIntType { + fn sim_value_eq(this: &SimValue, other: &SimValue>) -> bool { + this.hdl_eq(other) } } -impl SimValuePartialEq for AsyncReset { - fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { - **this == **other +impl SimValueEq for SIntType {} + +impl SimValuePartialOrd> for SIntType { + fn sim_value_partial_cmp( + this: &SimValue, + other: &SimValue>, + ) -> Option { + Some(this.hdl_cmp(other)) } } +impl SimValueOrd for SIntType { + fn sim_value_cmp(this: &SimValue, other: &SimValue) -> std::cmp::Ordering { + this.hdl_cmp(other) + } +} + +macro_rules! impl_sim_value_cmp_as_bool { + ($ty:ident) => { + impl SimValuePartialEq for $ty { + fn sim_value_eq(this: &SimValue, other: &SimValue) -> bool { + **this == **other + } + } + + impl SimValueEq for $ty {} + + impl SimValuePartialOrd for $ty { + fn sim_value_partial_cmp( + this: &SimValue, + other: &SimValue, + ) -> Option { + bool::partial_cmp(this, other) + } + } + + impl SimValueOrd for $ty { + fn sim_value_cmp(this: &SimValue, other: &SimValue) -> std::cmp::Ordering { + bool::cmp(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); + +macro_rules! forward_unary_op_to_value { + (#[trait = $($Trait:tt)+] fn $f:ident();) => { + impl<'a, T: Type, O: Type> $($Trait)+ for &'a SimValue + where + &'a T::SimValue: $($Trait)+>, + Valueless: $($Trait)+>, + { + type Output = SimValue; + + fn $f(self) -> Self::Output { + let ty = self.to_valueless().$f().ty(); + (&**self) + .$f() + .into_sim_value_with_type(self.to_valueless().$f().ty()) + } + } + + impl $($Trait)+ for SimValue + where + T::SimValue: $($Trait)+>, + Valueless: $($Trait)+>, + { + type Output = SimValue; + + fn $f(self) -> Self::Output { + let ty = self.to_valueless().$f().ty(); + SimValue::into_value(self) + .$f() + .into_sim_value_with_type(ty) + } + } + }; +} + +forward_unary_op_to_value! { + #[trait = std::ops::Neg] + fn neg(); +} + +forward_unary_op_to_value! { + #[trait = std::ops::Not] + fn not(); +} + #[doc(hidden)] pub mod match_sim_value { use crate::{ @@ -603,9 +723,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 +765,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 +797,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 +853,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 +865,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 +877,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 +897,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 +911,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 +953,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 +988,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 +1041,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 +1081,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 +1110,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 +1133,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 +1170,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 +1182,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 +1198,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 +1217,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 +1593,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,15 +1602,22 @@ 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) } } 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 3df80a8e..2424c03f 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 d9ea6b27..76c09555 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 057af466..f0956799 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 diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index 51f8cf7d..a350d9a4 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 a0392503..8765a4dd 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] diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index be4e2071..e55a5178 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); }