use quote::ToTokens;
use crate::apply::{Apply, check_expand_limit, err_ty, err_ty_at};
use crate::ast::*;
use crate::util::cartesian;
use proc_macro2::Span;
pub(crate) fn map_range(
start: usize, end: usize, inclusive: bool, span: Span, f: impl Fn(usize) -> Ty,
) -> Ty {
let end_mark = if inclusive { "=" } else { "" };
let ns = if inclusive {
(start..=end).collect::<Vec<_>>()
} else {
(start..end).collect::<Vec<_>>()
};
if ns.is_empty() {
return err_ty_at(
&format!(
"batch-impl: range `{}..{}{}` is empty (start not below end); no impls will be generated",
start, end, end_mark
),
span,
);
}
if let Some(e) = check_expand_limit(
&format!("range `{}..{}{}`", start, end, end_mark),
ns.len(),
) {
return e;
}
TyArray(ns.into_iter().map(f).collect()).into()
}
fn tuple_pow(mut elems: Vec<Ty>, n: usize) -> Ty {
if let Some(e) = check_expand_limit(&format!("tuple `^{}`", n), n) {
return e;
}
match elems.len() {
0 => pow_empty(n),
1 => pow_single(elems.remove(0), n),
_ => pow_cartesian(elems, n),
}
}
fn pow_empty(n: usize) -> Ty {
if n == 0 {
return TyTuple(vec![]).into();
}
let g = take_group();
let params = fresh_params(g, n);
let tp = TyTypeParam {
params: params.clone().into_iter().map(|p| (p.into(), None)).collect(),
bindings: vec![],
}
.to_ty();
tp.apply(TyTuple(params).into())
}
fn pow_single(template: Ty, n: usize) -> Ty {
let template_span = template.span;
if let TyKind::TypeParam(tp) = template.kind.clone() {
if tp.params.len() != 1 || tp.params[0].1.is_some() {
return err_ty(
"batch-impl: unexpected bound parameter in (<Trait>)⁁; this is an internal error",
);
}
let g = take_group();
let params = fresh_params(g, n);
let bound_ty = *tp.params[0].0.clone();
return TyTypeParam {
params: params
.clone()
.into_iter()
.map(|p| (p.into(), Some(bound_ty.clone())))
.collect(),
bindings: vec![],
}
.to_ty()
.with_span(template_span)
.apply(TyTuple(params).into());
}
TyTuple(
(0..n)
.map(|_| Ty { span: template_span, kind: template.kind.clone() })
.collect(),
)
.into()
}
fn pow_cartesian(elems: Vec<Ty>, n: usize) -> Ty {
let dims: Vec<Vec<Ty>> = std::iter::repeat_n(elems, n).collect();
let combos = cartesian(&dims);
if let Some(e) = check_expand_limit("tuple Cartesian product", combos.len()) {
return e;
}
TyArray(combos.into_iter().map(instantiate_combo).collect()).into()
}
fn instantiate_combo(elems: Vec<Ty>) -> Ty {
let g = take_group();
let mut tuple_elems = vec![];
let mut param_decls = vec![];
let mut pos = 0;
for elem in elems {
let elem_span = elem.span;
match elem.kind {
TyKind::TypeParam(tp) => {
let name = fresh_param(g, pos);
pos += 1;
let params = tp
.params
.iter()
.map(|(_, bound)| {
(TyPrimitive(name.clone()).to_ty().into(), bound.clone())
})
.collect();
param_decls.push(TyTypeParam { params, bindings: vec![] });
tuple_elems.push(TyPrimitive(name).to_ty().with_span(elem_span));
}
_ => tuple_elems.push(Ty { span: elem_span, kind: elem.kind }),
}
}
let tuple = TyTuple(tuple_elems).into();
if param_decls.is_empty() {
return tuple;
}
let merged = param_decls.into_iter().fold(
TyTypeParam { params: vec![], bindings: vec![] },
|mut acc, tp| {
acc.extend(tp);
acc
},
);
merged.to_ty().apply(tuple)
}
fn fresh_params(g: usize, n: usize) -> Vec<Ty> {
(0..n).map(|i| TyPrimitive(fresh_param(g, i)).into()).collect()
}
impl Apply for TyTuple {
fn apply_help(mut self, o: Ty, span: Span) -> Ty {
match o.kind {
TyKind::Num(TyNum(n)) => tuple_pow(self.0, n),
_ => {
self.0.push(o);
self.to_ty().with_span(span)
}
}
}
}
impl Apply for TyGroup {
fn apply_help(self, o: Ty, _span: Span) -> Ty {
self.0.apply(o)
}
}
impl Apply for TyFn {
fn apply_help(self, o: Ty, span: Span) -> Ty {
match self {
TyFn(None, None, is_unsafe) => match o.kind {
TyKind::Tuple(t) => {
TyFn(t.0.into(), None, is_unsafe).to_ty().with_span(span)
}
_ => err_ty_at(
"batch-impl: the right side of the `fn` prefix must be a tuple type, e.g. fn^(i32, u32)",
span,
),
},
TyFn(Some(params), None, is_unsafe) => {
TyFn(params.into(), o.into(), is_unsafe).to_ty().with_span(span)
}
TyFn(Some(_), Some(_), _) => err_ty_at(
"batch-impl: the `fn` type already has a return type; cannot apply again",
span,
),
TyFn(None, Some(_), _) => err_ty_at(
"batch-impl: the `fn` type is missing a parameter list; internal error",
span,
),
}
}
}
impl Apply for TyWithAttr {
fn apply_help(self, o: Ty, span: Span) -> Ty {
let inner = match self.1 {
Some(t) => t.apply(o),
None => o,
};
TyWithAttr(self.0, inner.into()).to_ty().with_span(span)
}
}
impl Apply for TyTypeParam {
fn apply_help(self, o: Ty, span: Span) -> Ty {
TyWithType(self, o.into()).to_ty().with_span(span)
}
}
impl Apply for TyNum {
fn apply_help(self, _: Ty, span: Span) -> Ty {
err_ty_at(
&format!(
"batch-impl: number `{}` cannot be a left operand; use it on the right (e.g. T^{})",
self.0, self.0
),
span,
)
}
}
impl Apply for TyRange {
fn apply_help(self, _: Ty, span: Span) -> Ty {
let end_mark = if self.inclusive { "=" } else { "" };
err_ty_at(
&format!(
"batch-impl: range `{}..{}{}` cannot be a left operand; it goes on the right (e.g. T^{}..{}{})",
self.start, self.end, end_mark, self.start, self.end, end_mark
),
span,
)
}
}
impl Apply for TyPrimitiveArray {
fn apply_help(self, o: Ty, span: Span) -> Ty {
match (self.0, self.1) {
(None, None) => TyPrimitiveArray(o.into(), None).to_ty().with_span(span),
(Some(elem), None) => {
TyPrimitiveArray(elem.into(), o.to_token_stream().into())
.to_ty()
.with_span(span)
}
_ => err_ty_at(
"batch-impl: fixed-size array `[T; N]` cannot be a left operand",
span,
),
}
}
}
macro_rules! impl_apply_optional_inner {
($ty:ident, $variant:ident) => {
impl Apply for $ty {
fn apply_help(self, o: Ty, span: Span) -> Ty {
let inner = match self.0 {
Some(t) => t.apply(o),
None => o,
};
$ty(inner.into(), self.1).to_ty().with_span(span)
}
}
};
}
macro_rules! impl_apply_inner {
($ty:ident, $variant:ident) => {
impl Apply for $ty {
fn apply_help(self, o: Ty, span: Span) -> Ty {
$ty(self.0, self.1.apply(o).into()).to_ty().with_span(span)
}
}
};
}
impl_apply_inner!(TyWithTrait, WithTrait);
impl_apply_inner!(TyWithType, WithType);
impl_apply_optional_inner!(TyWithCode, WithCode);
impl_apply_optional_inner!(TyWithWhere, WithWhere);