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,
};
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>) {
let mut flat = vec![];
let mut decl = None;
for e in elems {
let (mut es, d) = splat_expand(e);
flat.append(&mut es);
decl = merge_decls(decl, d);
}
(flat, decl)
}
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| (Box::new(e), 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| (Box::new(e), None)));
}
_ => flat.push((Box::new(inner), bound)),
}
}
_ => flat.push((name, bound)),
}
}
(flat, decl)
}
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(Some(e.into()))).collect())
}
Expand::Leaf(l) => Expand::Leaf(make(Some(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 mut combos: Vec<Vec<Ty>> = vec![vec![]];
for e in &t.0 {
let candidates = match &e.kind {
TyKind::Array(a) => a.0.clone(),
_ => vec![e.clone()],
};
let mut next =
Vec::with_capacity(combos.len() * candidates.len());
for ex in &combos {
for c in &candidates {
let mut combo = ex.clone();
combo.push(c.clone());
next.push(combo);
}
}
combos = next;
}
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 mut combos: Vec<TyParams> = vec![vec![]];
for (name, bound) in &g.1.params {
let candidates: TyParams = match &name.kind {
TyKind::Array(a) => {
a.0.iter()
.map(|e| (Box::new(e.clone()), bound.clone()))
.collect()
}
_ => vec![(name.clone(), bound.clone())],
};
let mut next =
Vec::with_capacity(combos.len() * candidates.len());
for ex in &combos {
for c in &candidates {
let mut combo = ex.clone();
combo.push(c.clone());
next.push(combo);
}
}
combos = next;
}
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 }),
}
}
}