use crate::apply::check_expand_limit;
use crate::ast::types::{
Ty, TyArray, TyFn, TyGeneric, TyGroup, TyKind, TyParams, TyPrimitiveArray,
TyTuple, TyTypeParam, TyWithAttr, TyWithCode, TyWithPrefix, TyWithTrait,
TyWithType, TyWithWhere,
};
use crate::util::cartesian;
pub(crate) enum Expand {
Leaf(Ty),
Many(Vec<Ty>),
}
pub(crate) fn splat_expand(ty: Ty) -> (Vec<Ty>, Option<TyTypeParam>) {
match ty.kind {
TyKind::Splat(s) => fold_splat_elems(s.elems().to_vec()),
TyKind::Array(a) => fold_splat_elems(a.0),
TyKind::Tuple(t) => {
(vec![Ty { span: ty.span, kind: TyKind::Tuple(t) }], None)
}
TyKind::Group(g) => splat_expand(*g.0),
TyKind::WithType(wt) => {
let TyWithType(params, inner) = wt;
let (elems, _) = match inner.kind {
TyKind::Tuple(t) => fold_splat_elems(t.0),
_ => splat_expand(*inner),
};
(elems, Some(params))
}
other => (vec![Ty { span: ty.span, kind: other }], None),
}
}
fn fold_splat_elems(elems: Vec<Ty>) -> (Vec<Ty>, Option<TyTypeParam>) {
elems.into_iter().fold((vec![], None), |(mut flat, decl), e| {
let (mut es, d) = splat_expand(e);
flat.append(&mut es);
(flat, merge_decls(decl, d))
})
}
pub(crate) fn flat_splat_params(params: TyParams) -> (TyParams, Option<TyTypeParam>) {
let mut flat = vec![];
let mut decl = None;
for (name, bound) in params {
match name.kind {
TyKind::Splat(_) => {
let (es, d) = splat_expand(*name);
decl = merge_decls(decl, d);
flat.extend(es.into_iter().map(|e| (e.into(), None)));
}
TyKind::WithType(wt) => {
decl = merge_decls(decl, Some(wt.0));
let inner = *wt.1;
match inner.kind {
TyKind::Splat(_) => {
let (es, d) = splat_expand(inner);
decl = merge_decls(decl, d);
flat.extend(es.into_iter().map(|e| (e.into(), None)));
}
_ => flat.push((inner.into(), bound)),
}
}
_ => flat.push((name, bound)),
}
}
(flat, decl)
}
pub(crate) fn contains_generator(params: &TyTypeParam) -> bool {
params.params.iter().any(|(n, _)| ty_contains_generator(n))
|| params
.bindings
.iter()
.any(|(n, v)| ty_contains_generator(n) || ty_contains_generator(v))
}
fn ty_contains_generator(ty: &Ty) -> bool {
match &ty.kind {
TyKind::WithType(_) => true,
TyKind::Generic(g) => {
ty_contains_generator(&g.0)
|| g.1.params.iter().any(|(n, b)| {
ty_contains_generator(n)
|| b.as_ref().is_some_and(ty_contains_generator)
})
|| g.1.bindings.iter().any(|(n, v)| {
ty_contains_generator(n) || ty_contains_generator(v)
})
}
TyKind::Array(a) => a.0.iter().any(ty_contains_generator),
TyKind::Tuple(t) => t.0.iter().any(ty_contains_generator),
TyKind::Splat(s) => s.elems().iter().any(ty_contains_generator),
TyKind::Group(g) => ty_contains_generator(&g.0),
TyKind::WithPrefix(w) => w.1.iter().any(|i| ty_contains_generator(i)),
TyKind::WithAttr(w) => w.1.iter().any(|i| ty_contains_generator(i)),
_ => false,
}
}
pub(crate) fn merge_decls(
a: Option<TyTypeParam>, b: Option<TyTypeParam>,
) -> Option<TyTypeParam> {
match (a, b) {
(None, b) => b,
(a, None) => a,
(Some(mut a), Some(b)) => {
a.extend(b);
Some(a)
}
}
}
fn expand_wrapped<F>(make: F, inner: Option<Box<Ty>>) -> Expand
where
F: Fn(Option<Box<Ty>>) -> Ty,
{
match inner {
Some(i) => match i.expand() {
Expand::Many(v) => {
Expand::Many(v.into_iter().map(|e| make(e.into())).collect())
}
Expand::Leaf(l) => Expand::Leaf(make(l.into())),
},
None => Expand::Leaf(make(None)),
}
}
fn expand_rebuild<F>(make: F, inner: Ty) -> Expand
where
F: Fn(Box<Ty>) -> Ty,
{
match inner.expand() {
Expand::Many(v) => {
Expand::Many(v.into_iter().map(|e| make(e.into())).collect())
}
Expand::Leaf(l) => Expand::Leaf(make(l.into())),
}
}
impl Ty {
#[allow(clippy::redundant_closure)] pub(crate) fn map_children(self, f: &mut impl FnMut(Ty) -> Ty) -> Ty {
let span = self.span;
match self.kind {
TyKind::Array(a) => TyArray(a.0.into_iter().map(|e| f(e)).collect())
.to_ty()
.with_span(span),
TyKind::Tuple(t) => TyTuple(t.0.into_iter().map(|e| f(e)).collect())
.to_ty()
.with_span(span),
TyKind::Group(g) => TyGroup(f(*g.0).into()).to_ty().with_span(span),
TyKind::PrimitiveArray(pa) => {
TyPrimitiveArray(pa.0.map(|e| f(*e).into()), pa.1)
.to_ty()
.with_span(span)
}
TyKind::Generic(g) => {
TyGeneric(f(*g.0).into(), g.1).to_ty().with_span(span)
}
TyKind::WithPrefix(wp) => {
TyWithPrefix(wp.0, wp.1.map(|e| f(*e).into())).to_ty().with_span(span)
}
TyKind::WithTrait(wt) => {
TyWithTrait(wt.0, f(*wt.1).into()).to_ty().with_span(span)
}
TyKind::WithCode(wc) => {
TyWithCode(wc.0.map(|e| f(*e).into()), wc.1).to_ty().with_span(span)
}
TyKind::WithWhere(ww) => {
TyWithWhere(ww.0.map(|e| f(*e).into()), ww.1).to_ty().with_span(span)
}
TyKind::WithType(wt) => {
TyWithType(wt.0, f(*wt.1).into()).to_ty().with_span(span)
}
TyKind::WithAttr(wa) => {
TyWithAttr(wa.0, wa.1.map(|e| f(*e).into())).to_ty().with_span(span)
}
TyKind::Fn(fn_) => TyFn(
fn_.0.map(|params| params.into_iter().map(|p| f(p)).collect()),
fn_.1.map(|r| f(*r).into()),
fn_.2,
)
.to_ty()
.with_span(span),
other => Ty { span, kind: other },
}
}
pub(crate) fn expand(self) -> Expand {
let Ty { span, kind } = self;
match kind {
TyKind::Array(ty) => Expand::Many(ty.0),
TyKind::Splat(s) => {
let (elems, decl) = splat_expand(s.to_ty().with_span(span));
Expand::Many(
elems
.into_iter()
.map(|e| match &decl {
Some(d) => TyWithType(d.clone(), e.into())
.to_ty()
.with_span(span),
None => e,
})
.collect(),
)
}
TyKind::Tuple(t) => {
if t.0.iter().any(|e| matches!(e.kind, TyKind::Array(_))) {
let dims: Vec<Vec<Ty>> =
t.0.iter()
.map(|e| match &e.kind {
TyKind::Array(a) => a.0.clone(),
_ => vec![e.clone()],
})
.collect();
let combos = cartesian(&dims);
if let Some(e) =
check_expand_limit("tuple list distribution", combos.len())
{
return e.expand();
}
Expand::Many(
combos
.into_iter()
.map(|combo| TyTuple(combo).to_ty().with_span(span))
.collect(),
)
} else {
Expand::Leaf(t.to_ty().with_span(span))
}
}
TyKind::WithCode(wc) => {
let TyWithCode(inner, payload) = wc;
expand_wrapped(
move |i| TyWithCode(i, payload.clone()).to_ty().with_span(span),
inner,
)
}
TyKind::WithWhere(ww) => {
let TyWithWhere(inner, payload) = ww;
expand_wrapped(
move |i| TyWithWhere(i, payload.clone()).to_ty().with_span(span),
inner,
)
}
TyKind::WithType(wt) => {
let TyWithType(params, inner) = wt;
expand_rebuild(
move |e| TyWithType(params.clone(), e).to_ty().with_span(span),
*inner,
)
}
TyKind::WithTrait(wt) => {
let TyWithTrait(t, inner) = wt;
expand_rebuild(
move |e| TyWithTrait(t.clone(), e).to_ty().with_span(span),
*inner,
)
}
TyKind::WithAttr(wa) => {
let TyWithAttr(attr, inner) = wa;
expand_wrapped(
move |i| TyWithAttr(attr.clone(), i).to_ty().with_span(span),
inner,
)
}
TyKind::WithPrefix(wp) => {
let TyWithPrefix(prefix, inner) = wp;
expand_wrapped(
move |i| TyWithPrefix(prefix, i).to_ty().with_span(span),
inner,
)
}
TyKind::Group(g) => (*g.0).expand(),
TyKind::Generic(g) => {
if g.1.params.iter().any(|(n, _)| matches!(n.kind, TyKind::Array(_)))
{
let dims: Vec<TyParams> =
g.1.params
.iter()
.map(|(name, bound)| match &name.kind {
TyKind::Array(a) => {
a.0.iter()
.map(|e| (e.clone().into(), bound.clone()))
.collect()
}
_ => vec![(name.clone(), bound.clone())],
})
.collect();
let combos = cartesian(&dims);
if let Some(e) =
check_expand_limit("generic array distribution", combos.len())
{
return e.expand();
}
Expand::Many(
combos
.into_iter()
.map(|params| {
TyGeneric(
g.0.clone(),
TyTypeParam {
params,
bindings: g.1.bindings.clone(),
},
)
.to_ty()
.with_span(span)
})
.collect(),
)
} else {
Expand::Leaf(Ty { span, kind: TyKind::Generic(g) })
}
}
other => Expand::Leaf(Ty { span, kind: other }),
}
}
}