use crate::{
expr::{ops::ArrayIndex, Expr, ToExpr},
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor},
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
},
util::ConstUsize,
};
use std::ops::Index;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
element: LazyInterned<T>,
len: Len::SizeType,
type_properties: TypeProperties,
}
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Array<{:?}, {}>", self.element, self.len())
}
}
pub type Array<T = CanonicalType, const LEN: usize = DYN_SIZE> = ArrayType<T, ConstUsize<LEN>>;
#[allow(non_upper_case_globals)]
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
#[allow(non_upper_case_globals)]
pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;
impl<T: Type, Len: Size> ArrayType<T, Len> {
const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties {
let TypeProperties {
is_passive,
is_storable,
is_castable_from_bits,
bit_width,
} = element;
let Some(bit_width) = bit_width.checked_mul(len) else {
panic!("array too big");
};
TypeProperties {
is_passive,
is_storable,
is_castable_from_bits,
bit_width,
}
}
pub fn new(element: T, len: Len::SizeType) -> Self {
let type_properties =
Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len));
Self {
element: LazyInterned::Interned(element.intern_sized()),
len,
type_properties,
}
}
pub fn element(&self) -> T {
*self.element
}
pub fn len(self) -> usize {
Len::as_usize(self.len)
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn type_properties(self) -> TypeProperties {
self.type_properties
}
pub fn as_dyn_array(self) -> Array {
Array::new_dyn(self.element().canonical(), self.len())
}
pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool {
self.len() == rhs.len()
&& self
.element()
.canonical()
.can_connect(rhs.element().canonical())
}
}
impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> {
pub fn new_static(element: T) -> Self {
Self::new(element, Len::SizeType::default())
}
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self {
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
len: Len::SIZE,
type_properties: Self::TYPE_PROPERTIES,
};
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
len: Len::SIZE,
type_properties: Self::MASK_TYPE_PROPERTIES,
};
const TYPE_PROPERTIES: TypeProperties =
Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE);
const MASK_TYPE_PROPERTIES: TypeProperties =
Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE);
}
impl<T: Type> Array<T> {
pub fn new_dyn(element: T, len: usize) -> Self {
Self::new(element, len)
}
}
impl<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
state.fold_array_type(self)
}
fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> {
Ok(ArrayType::new(self.element().fold(state)?, self.len))
}
}
impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
for ArrayType<T, Len>
{
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
state.visit_array_type(self)
}
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
self.element().visit(state)
}
}
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type BaseType = Array;
type MaskType = ArrayType<T::MaskType, Len>;
type MatchVariant = Len::ArrayMatch<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let base = Expr::as_dyn_array(this);
let base_ty = Expr::ty(base);
let _ = source_location;
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
std::iter::once(MatchVariantWithoutScope(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
}
fn mask_type(&self) -> Self::MaskType {
ArrayType::new(self.element().mask_type(), self.len)
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Array(array) = canonical_type else {
panic!("expected array");
};
Self::new(
T::from_canonical(array.element()),
Len::from_usize(array.len()),
)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
let base = Expr::as_dyn_array(*this);
let base_ty = Expr::ty(base);
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
Interned::into_inner(Intern::intern_sized(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct ArrayWithoutGenerics;
impl<T: Type> Index<T> for ArrayWithoutGenerics {
type Output = ArrayWithoutLen<T>;
fn index(&self, element: T) -> &Self::Output {
Interned::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayWithoutLen<T: Type> {
element: T,
}
impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
type Output = ArrayType<T, L::Size>;
fn index(&self, len: L) -> &Self::Output {
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
}
}