forked from libre-chip/fayalite
653 lines
21 KiB
Rust
653 lines
21 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
use crate::{
|
|
expr::{
|
|
target::{GetTarget, Target},
|
|
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
|
|
},
|
|
intern::{Intern, Interned, Memoize},
|
|
source_location::SourceLocation,
|
|
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
|
util::{interned_bit, ConstBool, ConstUsize, GenericConstBool, GenericConstUsize},
|
|
};
|
|
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec};
|
|
use num_bigint::{BigInt, BigUint, Sign};
|
|
use num_traits::{Signed, Zero};
|
|
use std::{
|
|
borrow::{BorrowMut, Cow},
|
|
fmt,
|
|
marker::PhantomData,
|
|
ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive},
|
|
sync::Arc,
|
|
};
|
|
|
|
mod sealed {
|
|
pub trait BoolOrIntTypeSealed {}
|
|
pub trait SizeSealed {}
|
|
}
|
|
|
|
pub const DYN_SIZE: usize = !0;
|
|
pub type DynSize = ConstUsize<DYN_SIZE>;
|
|
|
|
pub trait KnownSize: GenericConstUsize + Size<SizeType = Self> {
|
|
const SIZE: Self;
|
|
}
|
|
|
|
macro_rules! known_widths {
|
|
([] $($bits:literal)+) => {
|
|
impl KnownSize for ConstUsize<{
|
|
let v = 0;
|
|
$(let v = v * 2 + $bits;)*
|
|
v
|
|
}> {
|
|
const SIZE: Self = Self;
|
|
}
|
|
};
|
|
([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;
|
|
}
|
|
};
|
|
}
|
|
|
|
known_widths!([2 2 2 2 2 2 2 2 2]);
|
|
|
|
pub trait Size:
|
|
sealed::SizeSealed + Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static
|
|
{
|
|
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
|
|
+ AsMut<[Expr<Element>]>
|
|
+ BorrowMut<[Expr<Element>]>
|
|
+ 'static
|
|
+ Send
|
|
+ Sync
|
|
+ Eq
|
|
+ Clone
|
|
+ std::hash::Hash
|
|
+ std::fmt::Debug
|
|
+ IntoIterator<Item = Expr<Element>>
|
|
+ TryFrom<Vec<Expr<Element>>>
|
|
+ Into<Vec<Expr<Element>>>;
|
|
const KNOWN_VALUE: Option<usize>;
|
|
type SizeType: Copy + Ord + std::hash::Hash + std::fmt::Debug + Send + Sync + 'static;
|
|
fn as_usize(size_type: Self::SizeType) -> usize;
|
|
fn try_from_usize(v: usize) -> Option<Self::SizeType>;
|
|
#[track_caller]
|
|
fn from_usize(v: usize) -> Self::SizeType {
|
|
Self::try_from_usize(v).expect("wrong size")
|
|
}
|
|
}
|
|
|
|
impl Size for DynSize {
|
|
type ArrayMatch<Element: Type> = Box<[Expr<Element>]>;
|
|
const KNOWN_VALUE: Option<usize> = None;
|
|
type SizeType = usize;
|
|
|
|
fn as_usize(size_type: Self::SizeType) -> usize {
|
|
size_type
|
|
}
|
|
|
|
fn try_from_usize(v: usize) -> Option<Self::SizeType> {
|
|
Some(v)
|
|
}
|
|
}
|
|
|
|
impl<const VALUE: usize> sealed::SizeSealed for ConstUsize<VALUE> {}
|
|
|
|
impl<const VALUE: usize> Size for ConstUsize<VALUE>
|
|
where
|
|
ConstUsize<VALUE>: KnownSize,
|
|
{
|
|
type ArrayMatch<Element: Type> = [Expr<Element>; VALUE];
|
|
|
|
const KNOWN_VALUE: Option<usize> = Some(VALUE);
|
|
|
|
type SizeType = ConstUsize<VALUE>;
|
|
|
|
fn as_usize(_size_type: Self::SizeType) -> usize {
|
|
VALUE
|
|
}
|
|
|
|
fn try_from_usize(v: usize) -> Option<Self::SizeType> {
|
|
if v == VALUE {
|
|
Some(Self::SizeType::default())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_int {
|
|
($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => {
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
#[repr(transparent)]
|
|
pub struct $name<Width: Size = DynSize> {
|
|
pub width: Width::SizeType,
|
|
}
|
|
|
|
impl<Width: Size> fmt::Debug for $name<Width> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}<{}>", stringify!($pretty_name), self.width())
|
|
}
|
|
}
|
|
|
|
pub type $pretty_name<
|
|
const WIDTH: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
|
|
> = $name<ConstUsize<WIDTH>>;
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
pub const $pretty_name: $generic_name = $generic_name;
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
pub const $name: $generic_name = $generic_name;
|
|
|
|
impl<Width: Size> $name<Width> {
|
|
pub fn new(width: Width::SizeType) -> Self {
|
|
Self { width }
|
|
}
|
|
pub fn width(self) -> usize {
|
|
Width::as_usize(self.width)
|
|
}
|
|
pub fn type_properties(self) -> TypeProperties {
|
|
TypeProperties {
|
|
is_passive: true,
|
|
is_storable: true,
|
|
is_castable_from_bits: true,
|
|
bit_width: self.width(),
|
|
}
|
|
}
|
|
pub fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec {
|
|
BoolOrIntType::bits_from_bigint_wrapping(self, v)
|
|
}
|
|
pub fn from_bigint_wrapping(self, v: BigInt) -> $value<Width> {
|
|
$value {
|
|
bits: Arc::new(self.bits_from_bigint_wrapping(v)),
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
pub fn from_int_wrapping(self, v: impl Into<BigInt>) -> $value<Width> {
|
|
self.from_bigint_wrapping(v.into())
|
|
}
|
|
pub fn zero(self) -> $value<Width> {
|
|
self.from_int_wrapping(0u8)
|
|
}
|
|
pub fn can_connect<RhsWidth: Size>(self, _rhs: $name<RhsWidth>) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> sealed::BoolOrIntTypeSealed for $name<Width> {}
|
|
|
|
impl<Width: Size> BoolOrIntType for $name<Width> {
|
|
type Width = Width;
|
|
type Signed = ConstBool<$SIGNED>;
|
|
fn width(self) -> usize {
|
|
$name::width(self)
|
|
}
|
|
fn new(width: Width::SizeType) -> Self {
|
|
$name { width }
|
|
}
|
|
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
|
struct MemoizeBitsToExpr;
|
|
impl Memoize for MemoizeBitsToExpr {
|
|
type Input = BitSlice;
|
|
type InputOwned = BitVec;
|
|
type Output = Expr<$name>;
|
|
fn inner(self, input: &Self::Input) -> Self::Output {
|
|
$value::new(Arc::new(input.to_bitvec())).to_expr()
|
|
}
|
|
}
|
|
Expr::from_dyn_int(MemoizeBitsToExpr.get_cow(bits))
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> IntType for $name<Width> {
|
|
type Dyn = $name;
|
|
}
|
|
|
|
impl $name {
|
|
pub fn new_dyn(width: usize) -> Self {
|
|
Self { width }
|
|
}
|
|
pub fn bits_to_bigint(bits: &BitSlice) -> BigInt {
|
|
<Self as BoolOrIntType>::bits_to_bigint(bits)
|
|
}
|
|
}
|
|
|
|
impl<Width: KnownSize> $name<Width> {
|
|
pub fn new_static() -> Self {
|
|
Self {
|
|
width: Width::SizeType::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> Type for $name<Width> {
|
|
type BaseType = $pretty_name;
|
|
type MaskType = Bool;
|
|
impl_match_variant_as_self!();
|
|
fn mask_type(&self) -> Self::MaskType {
|
|
Bool
|
|
}
|
|
fn canonical(&self) -> CanonicalType {
|
|
CanonicalType::$pretty_name($name::new_dyn(self.width()))
|
|
}
|
|
#[track_caller]
|
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
|
let CanonicalType::$pretty_name(retval) = canonical_type else {
|
|
panic!("expected {}", stringify!($name));
|
|
};
|
|
$name {
|
|
width: Width::from_usize(retval.width),
|
|
}
|
|
}
|
|
fn source_location() -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
}
|
|
|
|
impl<Width: KnownSize> StaticType for $name<Width> {
|
|
const TYPE: Self = Self { width: Width::SIZE };
|
|
const MASK_TYPE: Self::MaskType = Bool;
|
|
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
|
is_passive: true,
|
|
is_storable: true,
|
|
is_castable_from_bits: true,
|
|
bit_width: Width::VALUE,
|
|
};
|
|
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
|
pub struct $generic_name;
|
|
|
|
impl Index<usize> for $generic_name {
|
|
type Output = $name;
|
|
|
|
fn index(&self, width: usize) -> &Self::Output {
|
|
Interned::<_>::into_inner(Intern::intern_sized($name::new_dyn(width)))
|
|
}
|
|
}
|
|
|
|
impl<const WIDTH: usize> Index<ConstUsize<WIDTH>> for $generic_name
|
|
where
|
|
ConstUsize<WIDTH>: KnownSize,
|
|
{
|
|
type Output = $pretty_name<WIDTH>;
|
|
|
|
fn index(&self, width: ConstUsize<WIDTH>) -> &Self::Output {
|
|
Interned::<_>::into_inner(Intern::intern_sized($name::new(width)))
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
|
pub struct $value<Width: Size = DynSize> {
|
|
bits: Arc<BitVec>,
|
|
_phantom: PhantomData<Width>,
|
|
}
|
|
|
|
impl<Width: Size> fmt::Debug for $value<Width> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let value = self.to_bigint();
|
|
let (sign, magnitude) = value.into_parts();
|
|
let sign = match sign {
|
|
Sign::Minus => "-",
|
|
Sign::NoSign | Sign::Plus => "",
|
|
};
|
|
let signed = if $SIGNED { "i" } else { "u" };
|
|
let width = self.width();
|
|
write!(f, "{sign}0x{magnitude:X}_{signed}{width}")
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> $value<Width> {
|
|
pub fn width(&self) -> usize {
|
|
if let Some(retval) = Width::KNOWN_VALUE {
|
|
debug_assert!(self.bits.len() == retval);
|
|
retval
|
|
} else {
|
|
self.bits.len()
|
|
}
|
|
}
|
|
pub fn from_bigint_wrapping(ty: $name<Width>, v: BigInt) -> $value<Width> {
|
|
ty.from_bigint_wrapping(v)
|
|
}
|
|
pub fn to_bigint(&self) -> BigInt {
|
|
$name::bits_to_bigint(&self.bits)
|
|
}
|
|
pub fn into_bits(self) -> Arc<BitVec> {
|
|
self.bits
|
|
}
|
|
pub fn ty(&self) -> $name<Width> {
|
|
$name {
|
|
width: Width::from_usize(self.width()),
|
|
}
|
|
}
|
|
pub fn as_dyn_int(self) -> $value {
|
|
$value {
|
|
bits: self.bits,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
pub fn bits(&self) -> &Arc<BitVec> {
|
|
&self.bits
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> ToLiteralBits for $value<Width> {
|
|
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
|
Ok(BitSlice::intern(&self.bits))
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> GetTarget for $value<Width> {
|
|
fn target(&self) -> Option<Interned<Target>> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<Width: Size> $value<Width> {
|
|
pub fn new(bits: Arc<BitVec>) -> Self {
|
|
Width::from_usize(bits.len()); // check that len is correct
|
|
Self {
|
|
bits,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Width: KnownSize> $value<Width> {
|
|
pub fn new_static(bits: Arc<BitVec>) -> Self {
|
|
assert!(bits.len() == Width::VALUE);
|
|
Self {
|
|
bits,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl $value {
|
|
pub fn new_dyn(bits: Arc<BitVec>) -> Self {
|
|
Self {
|
|
bits,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_int!(UInt, UIntType, UIntWithoutGenerics, UIntValue, false);
|
|
impl_int!(SInt, SIntType, SIntWithoutGenerics, SIntValue, true);
|
|
|
|
impl UInt {
|
|
/// gets the smallest `UInt` that fits `v` losslessly
|
|
pub fn for_value(v: impl Into<BigUint>) -> Self {
|
|
let v: BigUint = v.into();
|
|
Self::new(v.bits().try_into().expect("too big"))
|
|
}
|
|
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range(r: Range<impl Into<BigUint>>) -> Self {
|
|
let start: BigUint = r.start.into();
|
|
let end: BigUint = r.end.into();
|
|
assert!(!end.is_zero(), "empty range");
|
|
Self::range_inclusive(start..=(end - 1u8))
|
|
}
|
|
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self {
|
|
let (start, end) = r.into_inner();
|
|
let start: BigUint = start.into();
|
|
let end: BigUint = end.into();
|
|
assert!(start <= end, "empty range");
|
|
// no need to check `start`` since it's no larger than `end`
|
|
// so must not take more bits than `end`
|
|
Self::for_value(end)
|
|
}
|
|
}
|
|
|
|
impl SInt {
|
|
/// gets the smallest `SInt` that fits `v` losslessly
|
|
pub fn for_value(v: impl Into<BigInt>) -> Self {
|
|
let v: BigInt = v.into();
|
|
Self::new(
|
|
match v.sign() {
|
|
Sign::Minus => {
|
|
// account for sign bit and for the minimum value of an `SInt`
|
|
// being the negative of the maximum value minus one.
|
|
v.not().bits().checked_add(1).expect("too big")
|
|
}
|
|
Sign::NoSign => 0,
|
|
Sign::Plus => v.bits(),
|
|
}
|
|
.try_into()
|
|
.expect("too big"),
|
|
)
|
|
}
|
|
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range(r: Range<impl Into<BigInt>>) -> Self {
|
|
let start: BigInt = r.start.into();
|
|
let end: BigInt = r.end.into();
|
|
Self::range_inclusive(start..=(end - 1))
|
|
}
|
|
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range_inclusive(r: RangeInclusive<impl Into<BigInt>>) -> Self {
|
|
let (start, end) = r.into_inner();
|
|
let start: BigInt = start.into();
|
|
let end: BigInt = end.into();
|
|
assert!(start <= end, "empty range");
|
|
Self::new(Self::for_value(start).width.max(Self::for_value(end).width))
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_prim_int {
|
|
($prim_int:ident, $ty:ty) => {
|
|
impl ToExpr for $prim_int {
|
|
type Type = $ty;
|
|
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
<$ty>::le_bytes_to_expr_wrapping(
|
|
&self.to_le_bytes(),
|
|
<$ty as BoolOrIntType>::Width::VALUE,
|
|
)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
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>);
|
|
|
|
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
|
|
type Width: Size;
|
|
type Signed: GenericConstBool;
|
|
fn width(self) -> usize;
|
|
fn new(width: <Self::Width as Size>::SizeType) -> Self;
|
|
fn new_static() -> Self
|
|
where
|
|
Self::Width: KnownSize,
|
|
{
|
|
Self::new(<Self::Width as Size>::SizeType::default())
|
|
}
|
|
fn as_same_width_sint(self) -> SIntType<Self::Width> {
|
|
SIntType::new(Self::Width::from_usize(self.width()))
|
|
}
|
|
fn as_same_width_uint(self) -> UIntType<Self::Width> {
|
|
UIntType::new(Self::Width::from_usize(self.width()))
|
|
}
|
|
fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec {
|
|
let width = self.width();
|
|
let mut bytes = v.to_signed_bytes_le();
|
|
bytes.resize(
|
|
width.div_ceil(u8::BITS as usize),
|
|
if v.is_negative() { 0xFF } else { 0 },
|
|
);
|
|
let bitslice = &BitSlice::<u8, Lsb0>::from_slice(&bytes)[..width];
|
|
let mut bits = BitVec::new();
|
|
bits.extend_from_bitslice(bitslice);
|
|
bits
|
|
}
|
|
fn bits_to_bigint(bits: &BitSlice) -> BigInt {
|
|
let sign_byte = if Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false) {
|
|
0xFF
|
|
} else {
|
|
0
|
|
};
|
|
let mut bytes = vec![sign_byte; bits.len() / u8::BITS as usize + 1];
|
|
BitSlice::<u8, Lsb0>::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits);
|
|
BigInt::from_signed_bytes_le(&bytes)
|
|
}
|
|
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self>;
|
|
fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr<Self> {
|
|
let bitslice = BitSlice::<u8, Lsb0>::from_slice(&bytes);
|
|
let bitslice = &bitslice[..bit_width.min(bitslice.len())];
|
|
let mut bits = BitVec::new();
|
|
bits.extend_from_bitslice(bitslice);
|
|
bits.resize(
|
|
bit_width,
|
|
Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false),
|
|
);
|
|
Self::bits_to_expr(Cow::Owned(bits))
|
|
}
|
|
}
|
|
|
|
pub trait IntType: BoolOrIntType<BaseType = <Self as IntType>::Dyn> {
|
|
type Dyn: IntType<Dyn = Self::Dyn, Signed = Self::Signed, Width = DynSize>;
|
|
fn as_dyn_int(self) -> Self::Dyn {
|
|
Self::new_dyn(self.width())
|
|
}
|
|
fn try_from_dyn_int(value: Self::Dyn) -> Option<Self> {
|
|
Some(Self::new(Self::Width::try_from_usize(value.width())?))
|
|
}
|
|
#[track_caller]
|
|
fn from_dyn_int(value: Self::Dyn) -> Self {
|
|
Self::try_from_dyn_int(value).expect("wrong size")
|
|
}
|
|
fn new_dyn(width: usize) -> Self::Dyn {
|
|
Self::Dyn::new(width)
|
|
}
|
|
fn slice_index_to_range<I: RangeBounds<usize>>(self, index: I) -> Range<usize> {
|
|
let width = self.width();
|
|
let start = match index.start_bound() {
|
|
Bound::Included(start) => *start,
|
|
Bound::Excluded(start) => *start + 1,
|
|
Bound::Unbounded => 0,
|
|
};
|
|
let end = match index.end_bound() {
|
|
Bound::Included(end) => *end + 1,
|
|
Bound::Excluded(end) => *end,
|
|
Bound::Unbounded => width,
|
|
};
|
|
assert!(start <= end && end <= width, "slice range out-of-range");
|
|
start..end
|
|
}
|
|
fn slice_and_shift<I: RangeBounds<usize>>(self, index: I) -> (UInt, usize) {
|
|
let range = self.slice_index_to_range(index);
|
|
let width = range.end - range.start;
|
|
(UInt::new_dyn(width), range.start)
|
|
}
|
|
fn slice<I: RangeBounds<usize>>(self, index: I) -> UInt {
|
|
self.slice_and_shift(index).0
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
pub struct Bool;
|
|
|
|
impl sealed::BoolOrIntTypeSealed for Bool {}
|
|
|
|
impl BoolOrIntType for Bool {
|
|
type Width = ConstUsize<1>;
|
|
type Signed = ConstBool<false>;
|
|
|
|
fn width(self) -> usize {
|
|
1
|
|
}
|
|
|
|
fn new(width: <Self::Width as Size>::SizeType) -> Self {
|
|
let ConstUsize {} = width;
|
|
Bool
|
|
}
|
|
|
|
fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr<Self> {
|
|
assert_eq!(bits.len(), 1);
|
|
bits[0].to_expr()
|
|
}
|
|
}
|
|
|
|
impl Bool {
|
|
pub fn type_properties(self) -> TypeProperties {
|
|
Self::TYPE_PROPERTIES
|
|
}
|
|
pub fn can_connect(self, _rhs: Self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Type for Bool {
|
|
type BaseType = Bool;
|
|
type MaskType = Bool;
|
|
impl_match_variant_as_self!();
|
|
fn mask_type(&self) -> Self::MaskType {
|
|
Bool
|
|
}
|
|
fn canonical(&self) -> CanonicalType {
|
|
CanonicalType::Bool(Bool)
|
|
}
|
|
#[track_caller]
|
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
|
let CanonicalType::Bool(retval) = canonical_type else {
|
|
panic!("expected Bool");
|
|
};
|
|
retval
|
|
}
|
|
fn source_location() -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
}
|
|
|
|
impl StaticType for Bool {
|
|
const TYPE: Self = Bool;
|
|
const MASK_TYPE: Self::MaskType = Bool;
|
|
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
|
is_passive: true,
|
|
is_storable: true,
|
|
is_castable_from_bits: true,
|
|
bit_width: 1,
|
|
};
|
|
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
|
}
|
|
|
|
pub trait IntCmp<Rhs> {
|
|
fn cmp_eq(self, rhs: Rhs) -> Expr<Bool>;
|
|
fn cmp_ne(self, rhs: Rhs) -> Expr<Bool>;
|
|
fn cmp_lt(self, rhs: Rhs) -> Expr<Bool>;
|
|
fn cmp_le(self, rhs: Rhs) -> Expr<Bool>;
|
|
fn cmp_gt(self, rhs: Rhs) -> Expr<Bool>;
|
|
fn cmp_ge(self, rhs: Rhs) -> Expr<Bool>;
|
|
}
|
|
|
|
impl ToLiteralBits for bool {
|
|
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
|
Ok(interned_bit(*self))
|
|
}
|
|
}
|