1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#![no_std]
/// Default trait for returning something from a type-fn
pub trait TypeFn {
type Ret;
}
/// Verifies equality between two types at compile-time.
#[macro_export]
macro_rules! assert_types_eq {
($a:ty, $b:ty) => {{
let _: ::core::marker::PhantomData<$a> = ::core::marker::PhantomData::<$b>;
}};
}
/// Generates type-fn implementations.
/// Syntax:
/// `fn<$FnType$> $name$<$args$> $[$where-clause$]$ => $return-type$;`
///
/// FnType should simply be a trait containing `type Ret`, e.g. [`TypeFn`].
///
/// args is a type-arg list. To pattern-match types, use `T => Successor<T>` to
/// implement something like like `Add<Successor<T>, Rhs>`.
/// When implementing something for a specific type, use ` => TypeHere` (leave out
/// the type arg name).
/// If you need an extra type arg that wont be in the resulting type anywhere,
/// add a bar `|` and write type args there as normal.
///
/// The where clause is just like rust's, except you need to put a plus before the first
/// trait bound as well. Trailing commas are also mandatory.
/// Trait bounds can only be set using the where-clause.
///
/// The return type can use any of the type arguments.
///
/// You can define an arbitrary amount of functions in one macro invocation.
#[macro_export]
macro_rules! type_fn_impl {
(@a_or_else_b => ) => {compiler_error!()};
(@a_or_else_b => $($b:ident)+) => {$($b)*};
(@a_or_else_b $($a:ty)+ => $($b:ident)*) => { $($a)+ };
($(fn < $sup:ty > $name:ident <$($($arg:ident)? $(=> $($argv:ty)+)?),* $(| $($targ:ident),+)?>
$(where $($tv:ty : $( + $( ?$tcqm:ident )? $( $tc:ident )? )+ ,)+)? => $ret:ty;)+)
=> {
$(
impl<$($($arg, )?)* $($($targ, )*)?>
$sup for $name <$($crate::type_fn_impl!(@a_or_else_b $($($argv)*)? => $($arg)?)),*>
$(where $($tv : $($(?$tcqm)? $($tc)? + )+ ),+)?
{
type Ret = $ret;
}
)+
};
}
/// Creates type functions. You will still need to implement them yourself, e.g. using
/// [`type_fn_impl!`].
/// Syntax:
/// `$[$visibility$]$ fn $name$ <$args$>;`
///
/// visibility is just like rust's normal visibility modifier.
///
/// args is a list of type arguments. They can not have constraints at this time.
#[macro_export]
macro_rules! type_fn {
($($vis:vis fn $name:ident <$($arg:ident),*>;)*) => {
$(
$vis struct $name <$($arg),*> ($(::core::marker::PhantomData<$arg>, )*);
)*
};
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use crate::TypeFn;
#[test]
fn test_compile() {
struct Zero;
struct Succ<T>(PhantomData<T>);
type_fn! {
fn Add<Lhs, Rhs>;
fn Sub<Lhs, Rhs>;
fn Mul<Lhs, Rhs>;
}
type_fn_impl! {
fn<TypeFn> Add< => Zero, Rhs> => Rhs;
fn<TypeFn> Add<N => Succ<N>, Rhs>
where
Add<N, Rhs>: + TypeFn,
=> Succ<<Add<N, Rhs> as TypeFn>::Ret>;
fn<TypeFn> Sub<Lhs, => Zero> => Lhs;
fn<TypeFn> Sub<Lhs => Succ<Lhs>, Rhs => Succ<Rhs>>
where
Sub<Lhs, Rhs> : + TypeFn,
=> <Sub<Lhs, Rhs> as TypeFn>::Ret;
fn<TypeFn> Mul< => Zero, Rhs> => Zero;
fn<TypeFn> Mul<Lhs => Succ<Lhs>, Rhs>
where
Mul<Lhs, Rhs>: + TypeFn,
Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret>: + TypeFn,
=> <Add<Rhs, <Mul<Lhs, Rhs> as TypeFn>::Ret> as TypeFn>::Ret;
}
type TwoMinusOne = <Sub<Succ<Succ<Zero>>, Succ<Zero>> as TypeFn>::Ret;
assert_types_eq!(TwoMinusOne, Succ<Zero>);
assert_types_eq!(<Sub<TwoMinusOne, Succ<Zero>> as TypeFn>::Ret, Zero);
assert_types_eq!(
Succ<Succ<Succ<Succ<Zero>>>>,
<Mul<Succ<Succ<Zero>>, Succ<Succ<Zero>>> as TypeFn>::Ret
);
assert_types_eq!(Zero, <Mul<Succ<Succ<Zero>>, Zero> as TypeFn>::Ret);
}
}