use crate::{
array::{Array, ArrayType},
bundle::{Bundle, BundleField, BundleType},
clock::{Clock, ToClock},
enum_::{Enum, EnumType, EnumVariant},
expr::{
target::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,
},
CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits,
ToExpr, ToLiteralBits,
},
int::{
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt,
UIntType, UIntValue,
},
intern::{Intern, Interned},
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
ty::{CanonicalType, StaticType, Type},
util::ConstUsize,
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use num_bigint::BigInt;
use num_traits::{ToPrimitive, Zero};
use std::{
fmt,
marker::PhantomData,
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 {
(
#[generics($($generics:tt)*)]
#[value($Value:ty)]
$Trait:ident::$method:ident
) => {
impl<$($generics)*> $Trait for $Value
where
Expr<<$Value as ToExpr>::Type>: $Trait,
{
type Output = <Expr<<$Value as ToExpr>::Type> as $Trait>::Output;
fn $method(self) -> Self::Output {
$Trait::$method(self.to_expr())
}
}
};
}
macro_rules! impl_unary_op_trait {
(
#[generics($($generics:tt)*)]
fn $Trait:ident::$method:ident($arg:ident: $Arg:ty) -> $Output:ty {
$($body:tt)*
}
) => {
impl<$($generics)*> $Trait for Expr<$Arg>
{
type Output = Expr<$Output>;
fn $method(self) -> Self::Output {
let $arg = self;
$($body)*
}
}
};
}
macro_rules! impl_get_target_none {
([$($generics:tt)*] $ty:ty) => {
impl<$($generics)*> GetTarget for $ty {
fn target(&self) -> Option<Interned<Target>> {
None
}
}
};
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct NotU<Width: Size = DynSize> {
arg: Expr<UIntType<Width>>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<Width: Size> NotU<Width> {
pub fn new(arg: Expr<UIntType<Width>>) -> Self {
Self {
arg,
literal_bits: arg
.to_literal_bits()
.map(|bits| Intern::intern_owned(bits.to_bitvec().not())),
}
}
pub fn arg(self) -> Expr<UIntType<Width>> {
self.arg
}
}
impl<Width: Size> ToExpr for NotU<Width> {
type Type = UIntType<Width>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::NotU(NotU {
arg: Expr::as_dyn_int(self.arg),
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.arg.__ty,
__flow: Flow::Source,
}
}
}
impl<Width: Size> ToLiteralBits for NotU<Width> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([Width: Size] NotU<Width>);
impl_unary_op_trait! {
#[generics(Width: Size)]
fn Not::not(arg: UIntType<Width>) -> UIntType<Width> {
NotU::new(arg).to_expr()
}
}
forward_value_to_expr_unary_op_trait! {
#[generics(Width: Size)]
#[value(UIntValue<Width>)]
Not::not
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct NotS<Width: Size = DynSize> {
arg: Expr<SIntType<Width>>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<Width: Size> NotS<Width> {
pub fn new(arg: Expr<SIntType<Width>>) -> Self {
Self {
arg,
literal_bits: arg
.to_literal_bits()
.map(|bits| Intern::intern_owned(bits.to_bitvec().not())),
}
}
pub fn arg(self) -> Expr<SIntType<Width>> {
self.arg
}
}
impl<Width: Size> ToExpr for NotS<Width> {
type Type = UIntType<Width>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::NotS(NotS {
arg: Expr::as_dyn_int(self.arg),
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.arg.__ty.as_same_width_uint(),
__flow: Flow::Source,
}
}
}
impl<Width: Size> ToLiteralBits for NotS<Width> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([Width: Size] NotS<Width>);
impl_unary_op_trait! {
#[generics(Width: Size)]
fn Not::not(arg: SIntType<Width>) -> UIntType<Width> {
NotS::new(arg).to_expr()
}
}
forward_value_to_expr_unary_op_trait! {
#[generics(Width: Size)]
#[value(SIntValue<Width>)]
Not::not
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct NotB {
arg: Expr<Bool>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl NotB {
pub fn new(arg: Expr<Bool>) -> Self {
Self {
arg,
literal_bits: arg
.to_literal_bits()
.map(|bits| Intern::intern_owned(bits.to_bitvec().not())),
}
}
pub fn arg(self) -> Expr<Bool> {
self.arg
}
}
impl ToExpr for NotB {
type Type = Bool;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::NotB(NotB {
arg: self.arg,
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.arg.__ty,
__flow: Flow::Source,
}
}
}
impl ToLiteralBits for NotB {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] NotB);
impl_unary_op_trait! {
#[generics()]
fn Not::not(arg: Bool) -> Bool {
NotB::new(arg).to_expr()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Neg {
arg: Expr<SInt>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl Neg {
pub fn new(arg: Expr<SInt>) -> Self {
let mut retval = Self {
arg,
literal_bits: Err(NotALiteralExpr),
};
let result_ty = retval.ty();
retval.literal_bits = arg.to_literal_bits().map(|bits| {
Intern::intern_owned(result_ty.bits_from_bigint_wrapping(-SInt::bits_to_bigint(&bits)))
});
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<SInt> {
self.arg
}
}
impl ToExpr for Neg {
type Type = SInt;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::Neg(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl ToLiteralBits for Neg {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] Neg);
impl_unary_op_trait! {
#[generics(Width: Size)]
fn StdNeg::neg(arg: SIntType<Width>) -> SInt {
Neg::new(Expr::as_dyn_int(arg)).to_expr()
}
}
forward_value_to_expr_unary_op_trait! {
#[generics(Width: Size)]
#[value(SIntValue<Width>)]
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<Type = $Rhs>,
$($generics)*
> $Trait<Rhs> 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<Rhs> for $LhsValue
where
Expr<<$LhsValue as ToExpr>::Type>: $Trait<Rhs>,
{
type Output = <Expr<<$LhsValue as ToExpr>::Type> as $Trait<Rhs>>::Output;
fn $method(self, rhs: Rhs) -> Self::Output {
$Trait::$method(self.to_expr(), rhs)
}
}
};
}
fn binary_op_literal_bits<ResultTy: BoolOrIntType, Lhs: BoolOrIntType, Rhs: BoolOrIntType>(
result_ty: ResultTy,
lhs: Expr<Lhs>,
rhs: Expr<Rhs>,
f: impl FnOnce(BigInt, BigInt) -> Result<BigInt, NotALiteralExpr>,
) -> Result<Interned<BitSlice>, NotALiteralExpr> {
let lhs = lhs.to_literal_bits()?;
let rhs = rhs.to_literal_bits()?;
let lhs = Lhs::bits_to_bigint(&lhs);
let rhs = Rhs::bits_to_bigint(&rhs);
let result = f(lhs, rhs)?;
Ok(Intern::intern_owned(
result_ty.bits_from_bigint_wrapping(result),
))
}
macro_rules! binary_op_bitwise {
($name:ident, $ty:ident, bool, $Trait:ident::$method:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: Expr<$ty>) -> Self {
Self {
lhs,
rhs,
literal_bits: binary_op_literal_bits($ty, lhs, rhs, |lhs, rhs| {
Ok($Trait::$method(lhs, rhs))
}),
}
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
pub fn rhs(self) -> Expr<$ty> {
self.rhs
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: $ty,
__flow: Flow::Source,
}
}
}
impl $Trait for Expr<Bool> {
type Output = Expr<Bool>;
fn $method(self, rhs: Expr<Bool>) -> Expr<Bool> {
$name::new(self, rhs).to_expr()
}
}
};
($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: Expr<$ty>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
Ok($Trait::$method(lhs, rhs))
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = UInt;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn $Trait::$method(lhs: $ty<LhsWidth>, rhs: $ty<RhsWidth>) -> 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<LhsWidth>)]
$Trait::$method
}
};
}
binary_op_bitwise!(BitAndU, UIntType, UIntValue, BitAnd::bitand);
binary_op_bitwise!(BitAndS, SIntType, SIntValue, BitAnd::bitand);
binary_op_bitwise!(BitAndB, Bool, bool, BitAnd::bitand);
binary_op_bitwise!(BitOrU, UIntType, UIntValue, BitOr::bitor);
binary_op_bitwise!(BitOrS, SIntType, SIntValue, BitOr::bitor);
binary_op_bitwise!(BitOrB, Bool, bool, BitOr::bitor);
binary_op_bitwise!(BitXorU, UIntType, UIntValue, BitXor::bitxor);
binary_op_bitwise!(BitXorS, SIntType, SIntValue, BitXor::bitxor);
binary_op_bitwise!(BitXorB, Bool, bool, BitXor::bitxor);
macro_rules! binary_op_add_sub {
($name:ident, $ty:ident, $value:ident, $Trait:ident::$method:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: Expr<$ty>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
Ok($Trait::$method(lhs, rhs))
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn $Trait::$method(lhs: $ty<LhsWidth>, rhs: $ty<RhsWidth>) -> $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<LhsWidth>)]
$Trait::$method
}
};
}
binary_op_add_sub!(AddU, UIntType, UIntValue, Add::add);
binary_op_add_sub!(AddS, SIntType, SIntValue, Add::add);
binary_op_add_sub!(SubU, UIntType, UIntValue, Sub::sub);
binary_op_add_sub!(SubS, SIntType, SIntValue, Sub::sub);
macro_rules! binary_op_mul {
($name:ident, $ty:ident, $value:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: Expr<$ty>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
Ok(Mul::mul(lhs, rhs))
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn Mul::mul(lhs: $ty<LhsWidth>, rhs: $ty<RhsWidth>) -> $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<LhsWidth>)]
Mul::mul
}
};
}
binary_op_mul!(MulU, UIntType, UIntValue);
binary_op_mul!(MulS, SIntType, SIntValue);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DivU<LhsWidth: Size = DynSize> {
lhs: Expr<UIntType<LhsWidth>>,
rhs: Expr<UInt>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<LhsWidth: Size> DivU<LhsWidth> {
pub fn new(lhs: Expr<UIntType<LhsWidth>>, rhs: Expr<UInt>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
lhs.checked_div(&rhs).ok_or(NotALiteralExpr)
});
retval
}
pub fn lhs(self) -> Expr<UIntType<LhsWidth>> {
self.lhs
}
pub fn rhs(self) -> Expr<UInt> {
self.rhs
}
pub fn ty(self) -> UIntType<LhsWidth> {
Expr::ty(self.lhs)
}
}
impl<LhsWidth: Size> ToLiteralBits for DivU<LhsWidth> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([LhsWidth: Size] DivU<LhsWidth>);
impl<LhsWidth: Size> ToExpr for DivU<LhsWidth> {
type Type = UIntType<LhsWidth>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::DivU(DivU {
lhs: Expr::as_dyn_int(self.lhs),
rhs: self.rhs,
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn Div::div(lhs: UIntType<LhsWidth>, rhs: UIntType<RhsWidth>) -> UIntType<LhsWidth> {
DivU::new(lhs, Expr::as_dyn_int(rhs)).to_expr()
}
}
forward_value_to_expr_binary_op_trait! {
#[generics(LhsWidth: Size)]
#[lhs_value(UIntValue<LhsWidth>)]
Div::div
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DivS {
lhs: Expr<SInt>,
rhs: Expr<SInt>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl DivS {
pub fn new(lhs: Expr<SInt>, rhs: Expr<SInt>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
lhs.checked_div(&rhs).ok_or(NotALiteralExpr)
});
retval
}
pub fn lhs(self) -> Expr<SInt> {
self.lhs
}
pub fn rhs(self) -> Expr<SInt> {
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] DivS);
impl ToExpr for DivS {
type Type = SInt;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::DivS(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn Div::div(lhs: SIntType<LhsWidth>, rhs: SIntType<RhsWidth>) -> 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<LhsWidth>)]
Div::div
}
macro_rules! binary_op_rem {
($name:ident, $ty:ident, $value:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: Expr<$ty>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
if rhs.is_zero() {
Err(NotALiteralExpr)
} else {
Ok(lhs % rhs)
}
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl_binary_op_trait! {
#[generics(LhsWidth: Size, RhsWidth: Size)]
fn Rem::rem(lhs: $ty<LhsWidth>, rhs: $ty<RhsWidth>) -> $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<LhsWidth>)]
Rem::rem
}
};
}
binary_op_rem!(RemU, UIntType, UIntValue);
binary_op_rem!(RemS, SIntType, SIntValue);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct BundleLiteral<T: BundleType = Bundle> {
ty: T,
field_values: Interned<[Expr<CanonicalType>]>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<T: BundleType> BundleLiteral<T> {
#[track_caller]
pub fn new(ty: T, field_values: Interned<[Expr<CanonicalType>]>) -> Self {
let fields = ty.fields();
assert!(
Bundle::new(fields).type_properties().is_passive,
"bundle literal's type must be a passive type"
);
assert_eq!(fields.len(), field_values.len());
let mut literal_bits = Some(BitVec::new());
for (&field, &field_value) in fields.iter().zip(field_values.iter()) {
assert_eq!(
field.ty,
Expr::ty(field_value),
"field's type doesn't match value's type: field name {:?}",
field.name
);
if let (Some(literal_bits), Ok(field_bits)) =
(&mut literal_bits, field_value.to_literal_bits())
{
literal_bits.extend_from_bitslice(&field_bits);
} else {
literal_bits = None;
}
}
Self {
ty,
field_values,
literal_bits: literal_bits
.map(Intern::intern_owned)
.ok_or(NotALiteralExpr),
}
}
pub fn ty(self) -> T {
self.ty
}
pub fn field_values(self) -> Interned<[Expr<CanonicalType>]> {
self.field_values
}
}
impl<T: BundleType> ToLiteralBits for BundleLiteral<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([T: BundleType] BundleLiteral<T>);
impl<T: BundleType> ToExpr for BundleLiteral<T> {
type Type = T;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::BundleLiteral(BundleLiteral {
ty: Bundle::new(self.ty.fields()),
field_values: self.field_values,
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayLiteral<Element: Type, Len: Size> {
element_type: Element,
element_values: Interned<[Expr<CanonicalType>]>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
_phantom: PhantomData<Len>,
}
impl<Element: Type, Len: Size> ArrayLiteral<Element, Len> {
#[track_caller]
pub fn new(element_type: Element, element_values: Interned<[Expr<CanonicalType>]>) -> Self {
let canonical_element_type = element_type.canonical();
assert!(
canonical_element_type.is_passive(),
"array literal's type must be a passive type"
);
Len::from_usize(element_values.len()); let mut literal_bits = Some(BitVec::new());
for &element_value in element_values.iter() {
assert_eq!(
canonical_element_type,
Expr::ty(element_value),
"array's element type doesn't match element value's type",
);
if let (Some(literal_bits), Ok(element_bits)) =
(&mut literal_bits, element_value.to_literal_bits())
{
literal_bits.extend_from_bitslice(&element_bits);
} else {
literal_bits = None;
}
}
Self {
element_type,
element_values,
literal_bits: literal_bits
.map(Intern::intern_owned)
.ok_or(NotALiteralExpr),
_phantom: PhantomData,
}
}
pub fn element_type(self) -> Element {
self.element_type
}
pub fn len(self) -> usize {
self.element_values.len()
}
pub fn is_empty(self) -> bool {
self.element_values.is_empty()
}
pub fn ty(self) -> ArrayType<Element, Len> {
ArrayType::new(
self.element_type,
Len::from_usize(self.element_values.len()),
)
}
pub fn element_values(self) -> Interned<[Expr<CanonicalType>]> {
self.element_values
}
}
impl<Element: Type, Len: Size> ToLiteralBits for ArrayLiteral<Element, Len> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([Element: Type, Len: Size] ArrayLiteral<Element, Len>);
impl<Element: Type, Len: Size> ToExpr for ArrayLiteral<Element, Len> {
type Type = ArrayType<Element, Len>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::ArrayLiteral(ArrayLiteral {
element_type: self.element_type.canonical(),
element_values: self.element_values,
literal_bits: self.literal_bits,
_phantom: PhantomData,
})
.intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl<T: ToExpr<Type: StaticType>, const N: usize> ToExpr for [T; N]
where
ConstUsize<N>: KnownSize,
{
type Type = Array<T::Type, N>;
fn to_expr(&self) -> Expr<Self::Type> {
ArrayLiteral::new(
T::Type::TYPE,
self.iter().map(|v| Expr::canonical(v.to_expr())).collect(),
)
.to_expr()
}
}
impl<T: ToExpr<Type: StaticType>> ToExpr for [T] {
type Type = Array<T::Type>;
fn to_expr(&self) -> Expr<Self::Type> {
ArrayLiteral::new(
T::Type::TYPE,
self.iter().map(|v| Expr::canonical(v.to_expr())).collect(),
)
.to_expr()
}
}
impl<T: ToExpr<Type: StaticType>> ToExpr for Vec<T> {
type Type = Array<T::Type>;
fn to_expr(&self) -> Expr<Self::Type> {
<[T]>::to_expr(self)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EnumLiteral<T: EnumType = Enum> {
ty: T,
variants: Interned<[EnumVariant]>,
variant_index: usize,
variant_value: Option<Expr<CanonicalType>>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<T: EnumType> EnumLiteral<T> {
#[track_caller]
pub fn new_by_index(
ty: T,
variant_index: usize,
variant_value: Option<Expr<CanonicalType>>,
) -> Self {
let variants = ty.variants();
let canonical_ty = Enum::new(variants);
assert!(variant_index < variants.len());
assert_eq!(
variants[variant_index].ty,
variant_value.map(Expr::ty),
"variant's type doesn't match value's type: variant name {:?}",
variants[variant_index].name
);
let literal_bits = if let Ok(variant_value) = variant_value
.map(|variant_value| variant_value.to_literal_bits())
.transpose()
{
let mut literal_bits = BitVec::with_capacity(canonical_ty.type_properties().bit_width);
literal_bits.extend_from_bitslice(
&[variant_index].view_bits::<Lsb0>()[..canonical_ty.discriminant_bit_width()],
);
if let Some(variant_value) = variant_value {
literal_bits.extend_from_bitslice(&variant_value);
}
literal_bits.resize(canonical_ty.type_properties().bit_width, false);
Ok(Intern::intern_owned(literal_bits))
} else {
Err(NotALiteralExpr)
};
Self {
ty,
variants,
variant_index,
variant_value,
literal_bits,
}
}
#[track_caller]
pub fn new_by_name(
ty: T,
variant_name: Interned<str>,
variant_value: Option<Expr<CanonicalType>>,
) -> Self {
let enum_ty = Enum::new(ty.variants());
let Some(variant_index) = enum_ty.name_indexes().get(&variant_name) else {
panic!("unknown variant {variant_name:?}: in {ty:?}");
};
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
}
pub fn variant_name(self) -> Interned<str> {
self.variants[self.variant_index].name
}
pub fn variant_value(self) -> Option<Expr<CanonicalType>> {
self.variant_value
}
}
impl<T: EnumType> ToLiteralBits for EnumLiteral<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([T: EnumType] EnumLiteral<T>);
impl<T: EnumType> ToExpr for EnumLiteral<T> {
type Type = T;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::EnumLiteral(EnumLiteral {
ty: Enum::new(self.variants),
variants: self.variants,
variant_index: self.variant_index,
variant_value: self.variant_value,
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}
macro_rules! impl_dyn_shl {
($name:ident, $ty:ident, $value:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: Expr<UInt>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
#[track_caller]
pub fn new(lhs: Expr<$ty>, rhs: Expr<UInt>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
Ok(lhs << rhs.to_usize().ok_or(NotALiteralExpr)?)
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
pub fn rhs(self) -> Expr<UInt> {
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
#[track_caller]
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl<LhsWidth: Size, RhsWidth: Size> Shl<Expr<UIntType<RhsWidth>>> for Expr<$ty<LhsWidth>> {
type Output = Expr<$ty>;
fn shl(self, rhs: Expr<UIntType<RhsWidth>>) -> Self::Output {
$name::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs)).to_expr()
}
}
};
}
impl_dyn_shl!(DynShlU, UIntType, UIntValue);
impl_dyn_shl!(DynShlS, SIntType, SIntValue);
macro_rules! impl_dyn_shr {
($name:ident, $ty:ident, $value:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name<LhsWidth: Size = DynSize> {
lhs: Expr<$ty<LhsWidth>>,
rhs: Expr<UInt>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<LhsWidth: Size> $name<LhsWidth> {
#[track_caller]
pub fn new(lhs: Expr<$ty<LhsWidth>>, rhs: Expr<UInt>) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = binary_op_literal_bits(retval.ty(), lhs, rhs, |lhs, rhs| {
Ok(lhs << rhs.to_usize().ok_or(NotALiteralExpr)?)
});
retval
}
pub fn lhs(self) -> Expr<$ty<LhsWidth>> {
self.lhs
}
pub fn rhs(self) -> Expr<UInt> {
self.rhs
}
#[track_caller]
pub fn ty(self) -> $ty<LhsWidth> {
Expr::ty(self.lhs)
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl<LhsWidth: Size> ToExpr for $name<LhsWidth> {
type Type = $ty<LhsWidth>;
#[track_caller]
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name($name {
lhs: Expr::as_dyn_int(self.lhs),
rhs: self.rhs,
literal_bits: self.literal_bits,
})
.intern(),
__ty: Expr::ty(self.lhs),
__flow: Flow::Source,
}
}
}
impl<LhsWidth: Size, RhsWidth: Size> Shr<Expr<UIntType<RhsWidth>>> for Expr<$ty<LhsWidth>> {
type Output = Expr<$ty<LhsWidth>>;
fn shr(self, rhs: Expr<UIntType<RhsWidth>>) -> 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<usize> {
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) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$ty>,
rhs: usize,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$ty>, rhs: usize) -> Self {
let mut retval = Self {
lhs,
rhs,
literal_bits: Err(NotALiteralExpr),
};
retval.literal_bits = lhs.to_literal_bits().map(|bits| {
Intern::intern_owned(retval.ty().bits_from_bigint_wrapping($Trait::$method(
$ty::bits_to_bigint(&bits),
rhs,
)))
});
retval
}
pub fn lhs(self) -> Expr<$ty> {
self.lhs
}
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 {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $ty;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: self.ty(),
__flow: Flow::Source,
}
}
}
impl<LhsWidth: Size> $Trait<usize> for Expr<$ty<LhsWidth>> {
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<LhsWidth>)]
Shl::shl
}
forward_value_to_expr_binary_op_trait! {
#[generics(LhsWidth: Size)]
#[lhs_value(SIntValue<LhsWidth>)]
Shl::shl
}
forward_value_to_expr_binary_op_trait! {
#[generics(LhsWidth: Size)]
#[lhs_value(UIntValue<LhsWidth>)]
Shr::shr
}
forward_value_to_expr_binary_op_trait! {
#[generics(LhsWidth: Size)]
#[lhs_value(SIntValue<LhsWidth>)]
Shr::shr
}
pub trait ExprPartialEq<Rhs: Type>: Type {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
}
pub trait ExprPartialOrd<Rhs: Type>: ExprPartialEq<Rhs> {
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Rhs>) -> Expr<Bool>;
}
impl<Lhs: ToExpr, Rhs: ToExpr> HdlPartialEq<Rhs> for Lhs
where
Lhs::Type: ExprPartialEq<Rhs::Type>,
{
fn cmp_eq(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr())
}
fn cmp_ne(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr())
}
}
impl<Lhs: ToExpr, Rhs: ToExpr> HdlPartialOrd<Rhs> for Lhs
where
Lhs::Type: ExprPartialOrd<Rhs::Type>,
{
fn cmp_lt(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr())
}
fn cmp_le(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr())
}
fn cmp_gt(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr())
}
fn cmp_ge(self, rhs: Rhs) -> Expr<Bool> {
ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr())
}
}
macro_rules! impl_compare_op {
(
$(#[width($LhsWidth:ident, $RhsWidth:ident)])?
#[dyn_type($DynTy:ident)]
#[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)]
#[type($Lhs:ty, $Rhs:ty)]
#[trait($Trait:ident)]
$(
struct $name:ident;
fn $method:ident();
$CmpTrait:ident::$cmp_method:ident();
)*
) => {
$(#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
lhs: Expr<$DynTy>,
rhs: Expr<$DynTy>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(lhs: Expr<$DynTy>, rhs: Expr<$DynTy>) -> Self {
Self {
lhs,
rhs,
literal_bits: binary_op_literal_bits(Bool, lhs, rhs, |lhs, rhs| {
Ok($CmpTrait::$cmp_method(&lhs, &rhs).into())
}),
}
}
pub fn lhs(self) -> Expr<$DynTy> {
self.lhs
}
pub fn rhs(self) -> Expr<$DynTy> {
self.rhs
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = Bool;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: Bool,
__flow: Flow::Source,
}
}
})*
impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs {
$(fn $method($lhs: Expr<Self>, $rhs: Expr<$Rhs>) -> Expr<Bool> {
$name::new($dyn_lhs, $dyn_rhs).to_expr()
})*
}
};
}
impl_compare_op! {
#[dyn_type(Bool)]
#[to_dyn_type(lhs => lhs, rhs => rhs)]
#[type(Bool, Bool)]
#[trait(ExprPartialEq)]
struct CmpEqB; fn cmp_eq(); PartialEq::eq();
struct CmpNeB; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[dyn_type(Bool)]
#[to_dyn_type(lhs => lhs, rhs => rhs)]
#[type(Bool, Bool)]
#[trait(ExprPartialOrd)]
struct CmpLtB; fn cmp_lt(); PartialOrd::lt();
struct CmpLeB; fn cmp_le(); PartialOrd::le();
struct CmpGtB; fn cmp_gt(); PartialOrd::gt();
struct CmpGeB; fn cmp_ge(); PartialOrd::ge();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(UInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(UIntType<LhsWidth>, UIntType<RhsWidth>)]
#[trait(ExprPartialEq)]
struct CmpEqU; fn cmp_eq(); PartialEq::eq();
struct CmpNeU; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(UInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(UIntType<LhsWidth>, UIntType<RhsWidth>)]
#[trait(ExprPartialOrd)]
struct CmpLtU; fn cmp_lt(); PartialOrd::lt();
struct CmpLeU; fn cmp_le(); PartialOrd::le();
struct CmpGtU; fn cmp_gt(); PartialOrd::gt();
struct CmpGeU; fn cmp_ge(); PartialOrd::ge();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(SInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(SIntType<LhsWidth>, SIntType<RhsWidth>)]
#[trait(ExprPartialEq)]
struct CmpEqS; fn cmp_eq(); PartialEq::eq();
struct CmpNeS; fn cmp_ne(); PartialEq::ne();
}
impl_compare_op! {
#[width(LhsWidth, RhsWidth)]
#[dyn_type(SInt)]
#[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))]
#[type(SIntType<LhsWidth>, SIntType<RhsWidth>)]
#[trait(ExprPartialOrd)]
struct CmpLtS; fn cmp_lt(); PartialOrd::lt();
struct CmpLeS; fn cmp_le(); PartialOrd::le();
struct CmpGtS; fn cmp_gt(); PartialOrd::gt();
struct CmpGeS; fn cmp_ge(); PartialOrd::ge();
}
pub trait ExprCastTo<ToType: Type>: Type {
fn cast_to(src: Expr<Self>, to_type: ToType) -> Expr<ToType>;
}
impl ExprCastTo<Bool> for Bool {
fn cast_to(src: Expr<Self>, _to_type: Bool) -> Expr<Bool> {
src
}
}
macro_rules! impl_cast_int_op {
($name:ident, $from:ident, $to:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name<ToWidth: Size = DynSize> {
arg: Expr<$from>,
ty: $to<ToWidth>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<ToWidth: Size> $name<ToWidth> {
pub fn new(arg: Expr<$from>, ty: $to<ToWidth>) -> Self {
Self {
arg,
ty,
literal_bits: arg.to_literal_bits().map(|bits| {
Intern::intern_owned(
ty.bits_from_bigint_wrapping($from::bits_to_bigint(&bits)),
)
}),
}
}
pub fn arg(self) -> Expr<$from> {
self.arg
}
pub fn ty(self) -> $to<ToWidth> {
self.ty
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([ToWidth: Size] $name<ToWidth>);
impl<ToWidth: Size> ToExpr for $name<ToWidth> {
type Type = $to<ToWidth>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name($name {
arg: self.arg,
ty: self.ty.as_dyn_int(),
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}
impl<FromWidth: Size, ToWidth: Size> ExprCastTo<$to<ToWidth>> for $from<FromWidth> {
fn cast_to(src: Expr<Self>, to_type: $to<ToWidth>) -> Expr<$to<ToWidth>> {
$name::new(Expr::as_dyn_int(src), to_type).to_expr()
}
}
};
}
impl_cast_int_op!(CastUIntToUInt, UIntType, UIntType);
impl_cast_int_op!(CastUIntToSInt, UIntType, SIntType);
impl_cast_int_op!(CastSIntToUInt, SIntType, UIntType);
impl_cast_int_op!(CastSIntToSInt, SIntType, SIntType);
macro_rules! impl_cast_bit_op {
($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[trait] $Trait:ident::$trait_fn:ident $(,)?) => {
impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to);
impl $Trait for Expr<$from> {
fn $trait_fn(&self) -> Expr<$to> {
self.cast_to_static()
}
}
$(impl $Trait for Expr<$from_dyn> {
fn $trait_fn(&self) -> Expr<$to> {
self.cast_to_static()
}
})?
};
($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty, #[dyn] $to_dyn:ty $(,)?) => {
impl_cast_bit_op!($name, $from, $(#[dyn] $from_dyn,)? $to);
impl ExprCastTo<$to_dyn> for $from {
fn cast_to(src: Expr<Self>, to_type: $to_dyn) -> Expr<$to_dyn> {
assert!(to_type.width() == 1);
Expr::as_dyn_int(
$name::new(src).to_expr(),
)
}
}
$(impl ExprCastTo<$to_dyn> for $from_dyn {
fn cast_to(src: Expr<Self>, to_type: $to_dyn) -> Expr<$to_dyn> {
assert!(to_type.width() == 1);
Expr::as_dyn_int($name::new(Expr::from_dyn_int(src)).to_expr())
}
})?
};
($name:ident, $from:ty, $(#[dyn] $from_dyn:ty,)? $to:ty $(,)?) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
arg: Expr<$from>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(arg: Expr<$from>) -> Self {
Self {
arg,
literal_bits: arg.to_literal_bits(),
}
}
pub fn arg(self) -> Expr<$from> {
self.arg
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = $to;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: <$to>::TYPE,
__flow: Flow::Source,
}
}
}
impl ExprCastTo<$to> for $from {
fn cast_to(src: Expr<Self>, _to_type: $to) -> Expr<$to> {
$name::new(src).to_expr()
}
}
$(impl ExprCastTo<$to> for $from_dyn {
fn cast_to(src: Expr<Self>, _to_type: $to) -> Expr<$to> {
$name::new(Expr::<$from>::from_dyn_int(src)).to_expr()
}
})?
};
}
impl_cast_bit_op!(CastBoolToUInt, Bool, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastBoolToSInt, Bool, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastUIntToBool, UInt<1>, #[dyn] UInt, Bool);
impl_cast_bit_op!(CastSIntToBool, SInt<1>, #[dyn] SInt, Bool);
impl_cast_bit_op!(CastBoolToSyncReset, Bool, SyncReset, #[trait] ToSyncReset::to_sync_reset);
impl_cast_bit_op!(CastUIntToSyncReset, UInt<1>, #[dyn] UInt, SyncReset, #[trait] ToSyncReset::to_sync_reset);
impl_cast_bit_op!(CastSIntToSyncReset, SInt<1>, #[dyn] SInt, SyncReset, #[trait] ToSyncReset::to_sync_reset);
impl_cast_bit_op!(CastBoolToAsyncReset, Bool, AsyncReset, #[trait] ToAsyncReset::to_async_reset);
impl_cast_bit_op!(CastUIntToAsyncReset, UInt<1>, #[dyn] UInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset);
impl_cast_bit_op!(CastSIntToAsyncReset, SInt<1>, #[dyn] SInt, AsyncReset, #[trait] ToAsyncReset::to_async_reset);
impl_cast_bit_op!(CastSyncResetToBool, SyncReset, Bool);
impl_cast_bit_op!(CastSyncResetToUInt, SyncReset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastSyncResetToSInt, SyncReset, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastSyncResetToReset, SyncReset, Reset, #[trait] ToReset::to_reset);
impl_cast_bit_op!(CastAsyncResetToBool, AsyncReset, Bool);
impl_cast_bit_op!(CastAsyncResetToUInt, AsyncReset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastAsyncResetToSInt, AsyncReset, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastAsyncResetToReset, AsyncReset, Reset, #[trait] ToReset::to_reset);
impl_cast_bit_op!(CastResetToBool, Reset, Bool);
impl_cast_bit_op!(CastResetToUInt, Reset, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastResetToSInt, Reset, SInt<1>, #[dyn] SInt);
impl_cast_bit_op!(CastBoolToClock, Bool, Clock, #[trait] ToClock::to_clock);
impl_cast_bit_op!(CastUIntToClock, UInt<1>, #[dyn] UInt, Clock, #[trait] ToClock::to_clock);
impl_cast_bit_op!(CastSIntToClock, SInt<1>, #[dyn] SInt, Clock, #[trait] ToClock::to_clock);
impl_cast_bit_op!(CastClockToBool, Clock, Bool);
impl_cast_bit_op!(CastClockToUInt, Clock, UInt<1>, #[dyn] UInt);
impl_cast_bit_op!(CastClockToSInt, Clock, SInt<1>, #[dyn] SInt);
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct FieldAccess<FieldType: Type = CanonicalType> {
base: Expr<Bundle>,
field_index: usize,
field: BundleField,
field_type: FieldType,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
target: Option<Interned<Target>>,
}
impl<FieldType: Type + fmt::Debug> fmt::Debug for FieldAccess<FieldType> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.base.fmt(f)?;
f.write_str(".")?;
f.write_str(&self.field_name())
}
}
impl<FieldType: Type> FieldAccess<FieldType> {
#[track_caller]
pub fn new_by_index(base: Expr<Bundle>, field_index: usize) -> Self {
let field = Expr::ty(base).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]..][..field.ty.bit_width()].intern()
});
let target = base.target().map(|base| {
Intern::intern_sized(base.join(TargetPathElement::intern_sized(
TargetPathBundleField { name: field.name }.into(),
)))
});
Self {
base,
field_index,
field,
field_type,
literal_bits,
target,
}
}
#[track_caller]
pub fn new_by_name(base: Expr<Bundle>, name: Interned<str>) -> Self {
let base_ty = Expr::ty(base);
let Some(field_index) = base_ty.name_indexes().get(&name) else {
panic!("unknown field {name:?}: in {base_ty:?}");
};
Self::new_by_index(base, *field_index)
}
pub fn base(self) -> Expr<Bundle> {
self.base
}
pub fn field_index(self) -> usize {
self.field_index
}
pub fn field_name(self) -> Interned<str> {
self.field.name
}
pub fn field(self) -> BundleField {
self.field
}
pub fn field_type(self) -> FieldType {
self.field_type
}
}
impl<FieldType: Type> GetTarget for FieldAccess<FieldType> {
fn target(&self) -> Option<Interned<Target>> {
self.target
}
}
impl<FieldType: Type> ToLiteralBits for FieldAccess<FieldType> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl<FieldType: Type> ToExpr for FieldAccess<FieldType> {
type Type = FieldType;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::FieldAccess(FieldAccess {
base: self.base,
field_index: self.field_index,
field: self.field,
field_type: self.field_type.canonical(),
literal_bits: self.literal_bits,
target: self.target,
})
.intern(),
__ty: self.field_type,
__flow: Expr::flow(self.base).flip_if(self.field.flipped),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantAccess<VariantType: Type = CanonicalType> {
base: Expr<Enum>,
variant_index: usize,
variant: EnumVariant,
variant_type: Option<VariantType>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<VariantType: Type> VariantAccess<VariantType> {
#[track_caller]
pub fn new_by_index(base: Expr<Enum>, variant_index: usize) -> Self {
let variant = Expr::ty(base).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();
if bits[..discriminant_bit_width]
!= [variant_index].view_bits::<Lsb0>()[..discriminant_bit_width]
{
return Err(NotALiteralExpr);
}
Ok(bits[discriminant_bit_width..]
[..variant.ty.map(CanonicalType::bit_width).unwrap_or(0)]
.intern())
});
Self {
base,
variant_index,
variant,
variant_type,
literal_bits,
}
}
#[track_caller]
pub fn new_by_name(base: Expr<Enum>, name: Interned<str>) -> Self {
let base_ty = Expr::ty(base);
let Some(variant_index) = base_ty.name_indexes().get(&name) else {
panic!("unknown variant {name:?}: in {base_ty:?}");
};
Self::new_by_index(base, *variant_index)
}
pub fn base(self) -> Expr<Enum> {
self.base
}
pub fn variant_index(self) -> usize {
self.variant_index
}
pub fn variant_name(self) -> Interned<str> {
self.variant.name
}
pub fn variant(self) -> EnumVariant {
self.variant
}
pub fn variant_type(self) -> Option<VariantType> {
self.variant_type
}
}
impl<VariantType: Type> ToLiteralBits for VariantAccess<VariantType> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([VariantType: Type] VariantAccess<VariantType>);
impl<VariantType: Type> ToExpr for VariantAccess<VariantType> {
type Type = VariantType;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::VariantAccess(VariantAccess {
base: self.base,
variant_index: self.variant_index,
variant: self.variant,
variant_type: self.variant.ty,
literal_bits: self.literal_bits,
})
.intern(),
__ty: self
.variant_type
.unwrap_or_else(|| VariantType::from_canonical(().canonical())),
__flow: Flow::Source,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayIndex<ElementType: Type = CanonicalType> {
base: Expr<Array>,
element_index: usize,
element_type: ElementType,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
target: Option<Interned<Target>>,
}
impl<ElementType: Type> ArrayIndex<ElementType> {
#[track_caller]
pub fn new(base: Expr<Array>, element_index: usize) -> Self {
let base_ty = Expr::ty(base);
assert!(element_index < base_ty.len());
let element_type = ElementType::from_canonical(base_ty.element());
let literal_bits = base.to_literal_bits().map(|bits| {
let element_bit_width = base_ty.element().bit_width();
bits[element_bit_width * element_index..][..element_bit_width].intern()
});
let target = base.target().map(|base| {
Intern::intern_sized(
base.join(
TargetPathElement::ArrayElement(TargetPathArrayElement {
index: element_index,
})
.intern(),
),
)
});
Self {
base,
element_index,
element_type,
literal_bits,
target,
}
}
pub fn base(self) -> Expr<Array> {
self.base
}
pub fn element_index(self) -> usize {
self.element_index
}
pub fn element_type(self) -> ElementType {
self.element_type
}
}
impl<ElementType: Type> ToLiteralBits for ArrayIndex<ElementType> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl<ElementType: Type> GetTarget for ArrayIndex<ElementType> {
fn target(&self) -> Option<Interned<Target>> {
self.target
}
}
impl<ElementType: Type> ToExpr for ArrayIndex<ElementType> {
type Type = ElementType;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::ArrayIndex(ArrayIndex {
base: self.base,
element_index: self.element_index,
element_type: Expr::ty(self.base).element(),
literal_bits: self.literal_bits,
target: self.target,
})
.intern(),
__ty: self.element_type,
__flow: Expr::flow(self.base),
}
}
}
impl<ElementType: Type, Len: Size> ExprIndex<usize> for ArrayType<ElementType, Len> {
type Output = ElementType;
#[track_caller]
fn expr_index(this: &Expr<Self>, index: usize) -> &Expr<Self::Output> {
Interned::into_inner(
ArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), index)
.to_expr()
.intern_sized(),
)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct DynArrayIndex<ElementType: Type = CanonicalType> {
base: Expr<Array>,
element_index: Expr<UInt>,
element_type: ElementType,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
target: Option<Interned<Target>>,
}
impl<ElementType: Type> DynArrayIndex<ElementType> {
#[track_caller]
pub fn new(base: Expr<Array>, element_index: Expr<UInt>) -> Self {
let base_ty = Expr::ty(base);
let element_type = ElementType::from_canonical(base_ty.element());
let literal_bits = base
.to_literal_bits()
.ok()
.zip(element_index.to_literal_bits().ok())
.and_then(|(bits, index)| {
let index = UInt::bits_to_bigint(&index).to_usize()?;
if index >= base_ty.len() {
return None;
}
let element_bit_width = base_ty.element().bit_width();
Some(bits[element_bit_width * index..][..element_bit_width].intern())
})
.ok_or(NotALiteralExpr);
let target =
base.target().map(|base| {
Intern::intern_sized(base.join(
TargetPathElement::DynArrayElement(TargetPathDynArrayElement {}).intern(),
))
});
Self {
base,
element_index,
element_type,
literal_bits,
target,
}
}
pub fn base(self) -> Expr<Array> {
self.base
}
pub fn element_index(self) -> Expr<UInt> {
self.element_index
}
pub fn element_type(self) -> ElementType {
self.element_type
}
}
impl<ElementType: Type> ToLiteralBits for DynArrayIndex<ElementType> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl<ElementType: Type> GetTarget for DynArrayIndex<ElementType> {
fn target(&self) -> Option<Interned<Target>> {
self.target
}
}
impl<ElementType: Type> ToExpr for DynArrayIndex<ElementType> {
type Type = ElementType;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::DynArrayIndex(DynArrayIndex {
base: self.base,
element_index: self.element_index,
element_type: Expr::ty(self.base).element(),
literal_bits: self.literal_bits,
target: self.target,
})
.intern(),
__ty: self.element_type,
__flow: Expr::flow(self.base),
}
}
}
impl<ElementType: Type, Len: Size, Width: Size> ExprIndex<Expr<UIntType<Width>>>
for ArrayType<ElementType, Len>
{
type Output = ElementType;
fn expr_index(this: &Expr<Self>, index: Expr<UIntType<Width>>) -> &Expr<Self::Output> {
Interned::into_inner(
DynArrayIndex::<ElementType>::new(Expr::as_dyn_array(*this), Expr::as_dyn_int(index))
.to_expr()
.intern_sized(),
)
}
}
pub trait ExprIndex<I>: Type {
type Output: Type;
fn expr_index(this: &Expr<Self>, index: I) -> &Expr<Self::Output>;
}
impl<T: ExprIndex<I>, I> Index<I> for Expr<T> {
type Output = Expr<T::Output>;
#[track_caller]
fn index(&self, index: I) -> &Self::Output {
T::expr_index(self, index)
}
}
mod sealed {
pub trait BuiltInRangeSealed {}
}
pub trait BuiltInRange<T: ?Sized>: RangeBounds<T> + sealed::BuiltInRangeSealed {}
impl<I: ?Sized, T: RangeBounds<I> + ?Sized + sealed::BuiltInRangeSealed> BuiltInRange<I> for T {}
macro_rules! impl_built_in_range {
(<$T:ident> $ty:ty) => {
impl<$T> sealed::BuiltInRangeSealed for $ty {}
};
($ty:ty) => {
impl sealed::BuiltInRangeSealed for $ty {}
};
}
impl_built_in_range!(<T> (std::ops::Bound<T>, std::ops::Bound<T>));
impl_built_in_range!(<T> std::ops::Range<T>);
impl_built_in_range!(<T> std::ops::RangeFrom<T>);
impl_built_in_range!(std::ops::RangeFull);
impl_built_in_range!(<T> std::ops::RangeInclusive<T>);
impl_built_in_range!(<T> std::ops::RangeTo<T>);
impl_built_in_range!(<T> std::ops::RangeToInclusive<T>);
macro_rules! impl_int_slice {
($name:ident, $ty:ident) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct $name {
base: Expr<$ty>,
range_start: usize,
range_end: usize,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
base,
range_start: _,
range_end: _,
literal_bits,
} = self;
f.debug_struct(stringify!($name))
.field("base", base)
.field("range", &self.range())
.field("literal_bits", literal_bits)
.finish()
}
}
impl $name {
pub fn new(base: Expr<$ty>, range: Range<usize>) -> Self {
assert!(
range.start <= range.end && range.end <= Expr::ty(base).width(),
"invalid range"
);
let literal_bits = base
.to_literal_bits()
.map(|bits| bits[range.clone()].intern());
Self {
base,
range_start: range.start,
range_end: range.end,
literal_bits,
}
}
pub fn base(self) -> Expr<$ty> {
self.base
}
pub fn range(self) -> Range<usize> {
self.range_start..self.range_end
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = UInt;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: UInt::new_dyn(self.range().len()),
__flow: Flow::Source,
}
}
}
impl<Width: Size, I: BuiltInRange<usize>> ExprIndex<I> for $ty<Width> {
type Output = UInt;
#[track_caller]
fn expr_index(this: &Expr<Self>, index: I) -> &Expr<Self::Output> {
let base = Expr::as_dyn_int(*this);
let base_ty = Expr::ty(base);
let range = base_ty.slice_index_to_range(index);
Interned::into_inner($name::new(base, range).to_expr().intern_sized())
}
}
impl<Width: Size> ExprIndex<usize> for $ty<Width> {
type Output = Bool;
#[track_caller]
fn expr_index(this: &Expr<Self>, index: usize) -> &Expr<Self::Output> {
let base = Expr::as_dyn_int(*this);
let base_ty = Expr::ty(base);
assert!(index < base_ty.width());
Interned::into_inner(
$name::new(base, index..(index + 1))
.to_expr()
.cast_to_static::<Bool>()
.intern_sized(),
)
}
}
};
}
impl_int_slice!(SliceUInt, UIntType);
impl_int_slice!(SliceSInt, SIntType);
macro_rules! impl_reduce_bits {
($name:ident, $ty:ident, |$bits:ident| $reduction_op:expr) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
arg: Expr<$ty>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl $name {
pub fn new(arg: Expr<$ty>) -> Self {
let literal_bits = arg.to_literal_bits().and_then(|$bits| $reduction_op);
Self { arg, literal_bits }
}
pub fn arg(self) -> Expr<$ty> {
self.arg
}
}
impl ToLiteralBits for $name {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] $name);
impl ToExpr for $name {
type Type = UInt<1>;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::$name(*self).intern(),
__ty: UInt::<1>::TYPE,
__flow: Flow::Source,
}
}
}
};
}
impl_reduce_bits!(ReduceBitAndU, UInt, |bits| bits.all().to_literal_bits());
impl_reduce_bits!(ReduceBitAndS, SInt, |bits| bits.all().to_literal_bits());
impl_reduce_bits!(ReduceBitOrU, UInt, |bits| bits.any().to_literal_bits());
impl_reduce_bits!(ReduceBitOrS, SInt, |bits| bits.any().to_literal_bits());
impl_reduce_bits!(ReduceBitXorU, UInt, |bits| (bits.count_ones() % 2 != 0)
.to_literal_bits());
impl_reduce_bits!(ReduceBitXorS, SInt, |bits| (bits.count_ones() % 2 != 0)
.to_literal_bits());
impl<Width: Size> ReduceBits for Expr<UIntType<Width>> {
type UIntOutput = Expr<UInt<1>>;
type BoolOutput = Expr<Bool>;
fn reduce_bitand(self) -> Self::UIntOutput {
ReduceBitAndU::new(Expr::as_dyn_int(self)).to_expr()
}
fn reduce_bitor(self) -> Self::UIntOutput {
ReduceBitOrU::new(Expr::as_dyn_int(self)).to_expr()
}
fn reduce_bitxor(self) -> Self::UIntOutput {
ReduceBitXorU::new(Expr::as_dyn_int(self)).to_expr()
}
fn any_one_bits(self) -> Self::BoolOutput {
self.reduce_bitor().cast_to_static()
}
fn any_zero_bits(self) -> Self::BoolOutput {
(!self.reduce_bitand()).cast_to_static()
}
fn all_one_bits(self) -> Self::BoolOutput {
self.reduce_bitand().cast_to_static()
}
fn all_zero_bits(self) -> Self::BoolOutput {
(!self.reduce_bitor()).cast_to_static()
}
fn parity_odd(self) -> Self::BoolOutput {
self.reduce_bitxor().cast_to_static()
}
fn parity_even(self) -> Self::BoolOutput {
(!self.reduce_bitxor()).cast_to_static()
}
}
impl<Width: Size> ReduceBits for Expr<SIntType<Width>> {
type UIntOutput = Expr<UInt<1>>;
type BoolOutput = Expr<Bool>;
fn reduce_bitand(self) -> Self::UIntOutput {
ReduceBitAndS::new(Expr::as_dyn_int(self)).to_expr()
}
fn reduce_bitor(self) -> Self::UIntOutput {
ReduceBitOrS::new(Expr::as_dyn_int(self)).to_expr()
}
fn reduce_bitxor(self) -> Self::UIntOutput {
ReduceBitXorS::new(Expr::as_dyn_int(self)).to_expr()
}
fn any_one_bits(self) -> Self::BoolOutput {
self.reduce_bitor().cast_to_static()
}
fn any_zero_bits(self) -> Self::BoolOutput {
(!self.reduce_bitand()).cast_to_static()
}
fn all_one_bits(self) -> Self::BoolOutput {
self.reduce_bitand().cast_to_static()
}
fn all_zero_bits(self) -> Self::BoolOutput {
(!self.reduce_bitor()).cast_to_static()
}
fn parity_odd(self) -> Self::BoolOutput {
self.reduce_bitxor().cast_to_static()
}
fn parity_even(self) -> Self::BoolOutput {
(!self.reduce_bitxor()).cast_to_static()
}
}
impl ReduceBits for Expr<Bool> {
type UIntOutput = Expr<UInt<1>>;
type BoolOutput = Expr<Bool>;
fn reduce_bitand(self) -> Self::UIntOutput {
self.cast_to_static()
}
fn reduce_bitor(self) -> Self::UIntOutput {
self.cast_to_static()
}
fn reduce_bitxor(self) -> Self::UIntOutput {
self.cast_to_static()
}
fn any_one_bits(self) -> Self::BoolOutput {
self
}
fn any_zero_bits(self) -> Self::BoolOutput {
!self
}
fn all_one_bits(self) -> Self::BoolOutput {
self
}
fn all_zero_bits(self) -> Self::BoolOutput {
!self
}
fn parity_odd(self) -> Self::BoolOutput {
self
}
fn parity_even(self) -> Self::BoolOutput {
!self
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct CastToBits {
arg: Expr<CanonicalType>,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl CastToBits {
pub fn new(arg: Expr<CanonicalType>) -> Self {
Self {
arg,
literal_bits: arg.to_literal_bits(),
}
}
pub fn arg(self) -> Expr<CanonicalType> {
self.arg
}
}
impl ToLiteralBits for CastToBits {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([] CastToBits);
impl ToExpr for CastToBits {
type Type = UInt;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::CastToBits(*self).intern(),
__ty: UInt::new_dyn(Expr::ty(self.arg).bit_width()),
__flow: Flow::Source,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct CastBitsTo<T: Type = CanonicalType> {
arg: Expr<UInt>,
ty: T,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl<T: Type> CastBitsTo<T> {
#[track_caller]
pub fn new(arg: Expr<UInt>, 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);
Self {
arg,
ty,
literal_bits: arg.to_literal_bits(),
}
}
pub fn arg(self) -> Expr<UInt> {
self.arg
}
pub fn ty(self) -> T {
self.ty
}
}
impl<T: Type> ToLiteralBits for CastBitsTo<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
self.literal_bits
}
}
impl_get_target_none!([T: Type] CastBitsTo<T>);
impl<T: Type> ToExpr for CastBitsTo<T> {
type Type = T;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::CastBitsTo(CastBitsTo {
arg: self.arg,
ty: self.ty.canonical(),
literal_bits: self.literal_bits,
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Uninit<T: Type = CanonicalType> {
ty: T,
}
impl<T: Type> Uninit<T> {
#[track_caller]
pub fn new(ty: T) -> Self {
Self { ty }
}
pub fn ty(self) -> T {
self.ty
}
}
impl<T: Type> ToLiteralBits for Uninit<T> {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Err(NotALiteralExpr)
}
}
impl_get_target_none!([T: Type] Uninit<T>);
impl<T: Type> ToExpr for Uninit<T> {
type Type = T;
fn to_expr(&self) -> Expr<Self::Type> {
Expr {
__enum: ExprEnum::Uninit(Uninit {
ty: self.ty.canonical(),
})
.intern(),
__ty: self.ty,
__flow: Flow::Source,
}
}
}