pub trait Contains<T> {
const ITEM: T;
}
pub trait ConstFn<Arg, Ret> {
type Body<T: Contains<Arg>>: Contains<Ret>;
}
#[macro_export]
macro_rules! const_fn {
($(#[$attr:meta])* $vis:vis fn $fn_name:ident($($rest:tt)+) -> $ret_type:ty $body:block) => {
$crate::const_fn!(
@parse_args
ret_type: $ret_type,
body: $body,
rest: [$($rest)+],
{
attrs: [$(#[$attr])*],
vis: $vis,
name: $fn_name,
}
);
};
($(#[$attr:meta])* $vis:vis fn $fn_name:ident<$($rest:tt)+) => {
$crate::const_fn!(
@parse_generic
[$($rest)+]
{ [] [] [(),] }
{
attrs: [$(#[$attr])*],
vis: $vis,
name: $fn_name,
}
);
};
(
@parse_generic
[const $N:ident: $Type:ty, $($rest:tt)+ ]
{ [$($type_generics:tt)*] [$($impl_generics:tt)*] [$($phantom_generics:tt)*] }
{ $($passthrough:tt)* }
) => {
$crate::const_fn!(
@parse_generic
[$($rest)+]
{
[$($type_generics)* $N, ]
[$($impl_generics)* const $N: $Type, ]
[$($phantom_generics)*]
}
{ $($passthrough)* }
);
};
(
@parse_generic
[const $N:ident: $Type:ty >($($rest:tt)+) -> $ret_type:ty $body:block ]
{ [$($type_generics:tt)*] [$($impl_generics:tt)*] [$($phantom_generics:tt)*] }
{ $($passthrough:tt)* }
) => {
$crate::const_fn!(
@parse_args
[$($type_generics)* $N, ],
[$($impl_generics)* const $N: $Type, ],
[$($phantom_generics)*],
ret_type: $ret_type,
body: $body,
rest: [$($rest)+],
{$($passthrough)*}
);
};
(
@parse_generic
[$T:ident $(: $bound:path)?, $($rest:tt)+ ]
{ [$($type_generics:tt)*] [$($impl_generics:tt)*] [$($phantom_generics:tt)*] }
{ $($passthrough:tt)* }
) => {
$crate::const_fn!(
@parse_generic
[$($rest)+]
{
[$($type_generics)* $T, ]
[$($impl_generics)* $T $(: $bound)?, ]
[$($phantom_generics)* $T,]
}
{ $($passthrough)* }
);
};
(
@parse_generic
[$T:ident $(: $bound:path)? >($($rest:tt)+) -> $ret_type:ty $body:block ]
{ [$($type_generics:tt)*] [$($impl_generics:tt)*] [$($phantom_generics:tt)*] }
{ $($passthrough:tt)* }
) => {
$crate::const_fn!(
@parse_args
[$($type_generics)* $T, ],
[$($impl_generics)* $T $(: $bound)?, ],
[$($phantom_generics)* $T,],
ret_type: $ret_type,
body: $body,
rest: [$($rest)+],
{$($passthrough)*}
);
};
(
@parse_generic
[> ($($rest:tt)+) -> $ret_type:ty $body:block]
{ [$($type_generics:tt)*] [$($impl_generics:tt)*] [$($phantom_generics:tt)*] }
{ $($passthrough:tt)* }
) => {
$crate::const_fn!(
@parse_args
[$($type_generics)+],
[$($impl_generics)+],
[$($phantom_generics)*],
ret_type: $ret_type,
body: $body,
rest: [$($rest)+],
{$($passthrough)*}
);
};
(
@parse_args
$(
[$($type_generics:tt)+],
[$($impl_generics:tt)+],
[$($phantom_generics:tt)*],
)?
ret_type: $RetType:ty,
body: $body:block,
rest: [$( $arg_name:tt : $ArgType:ty ),+ $(,)?],
{
attrs: [$($attr:tt)*],
vis: $vis:vis,
name: $name:ident,
}
) => {
$($attr)* $vis const fn $name $(< $($impl_generics)+ >)?($( $arg_name : $ArgType ),+) -> $RetType $body
$crate::raw_const_fn!(
attrs: [
#[doc = concat!("`ConstFn` version of [`", stringify!($name), "`](fn@", stringify!($name), ")")]
#[allow(non_camel_case_types)]
],
vis: $vis,
name: $name,
$(
type_generics: [$($type_generics)+],
impl_generics: [$($impl_generics)+],
phantom_generics: ($($phantom_generics)*),
)?
arg_type: ($($ArgType,)+),
arg_param: Arg,
ret_type: $RetType,
body: {
let ($($arg_name,)+) = Arg::ITEM;
$name $( ::<$($type_generics)+> )? ($($arg_name,)*)
},
);
};
(
@parse_args
$(
[$($type_generics:tt)+],
[$($impl_generics:tt)+],
[$($phantom_generics:tt)*],
)?
ret_type: $RetType:ty,
body: $body:block,
rest: [#[raw] $arg_name:ident: $ArgType:ty],
{
attrs: [$($attr:tt)*],
vis: $vis:vis,
name: $name:ident,
}
) => {
$crate::raw_const_fn!(
attrs: [
$($attr)*
#[allow(non_camel_case_types)]
],
vis: $vis,
name: $name,
$(
type_generics: [$($type_generics)+],
impl_generics: [$($impl_generics)+],
phantom_generics: ($($phantom_generics)*),
)?
arg_type: $ArgType,
arg_param: $arg_name,
ret_type: $RetType,
body: $body,
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! raw_const_fn {
(
attrs: [$(#[$attr:meta])*],
vis: $vis:vis,
name: $Name:ident,
$(
type_generics: [$($TG:tt)+],
impl_generics: [$($IG:tt)+],
phantom_generics: $PG:ty,
)?
arg_type: $ArgType:ty,
arg_param: $Arg:ident,
ret_type: $RetType:ty,
body: $body:block,
) => {
$(#[$attr])*
$vis struct $Name $(< $($IG)+ >)? {
$( phantom: ::core::marker::PhantomData<$PG> )?
}
const _: () = {
impl $( < $($IG)+ > )? $crate::fields::utils::const_fn::ConstFn<$ArgType, $RetType> for $Name $( < $($TG)+ > )? {
type Body<Arg: $crate::fields::utils::const_fn::Contains<$ArgType>> = Body<(Self, Arg)>;
}
$vis struct Body<T>(::std::marker::PhantomData<T>);
impl<$Arg: $crate::fields::utils::const_fn::Contains<$ArgType> $( , $($IG)+)?>
$crate::fields::utils::const_fn::Contains<$RetType> for Body<($Name $( < $($TG)+ > )?, $Arg)>
{
const ITEM: $RetType = $body;
}
};
};
}
macro_rules! impl_tuples {
($( ($($C:ident : $T:ident),+) ),+$(,)?) => {$(
impl<$($T, $C: Contains<$T>),+> Contains<($($T,)+)> for ($($C,)+) {
const ITEM: ($($T,)+) = ($($C::ITEM,)+);
}
)+};
}
impl_tuples! [
(C1: T1),
(C1: T1, C2: T2),
(C1: T1, C2: T2, C3: T3),
(C1: T1, C2: T2, C3: T3, C4: T4),
(C1: T1, C2: T2, C3: T3, C4: T4, C5: T5),
];
#[cfg(debug_assertions)]
#[allow(dead_code)]
mod compile_tests {
const_fn! {
fn non_generic(_arg: ()) -> () {}
}
const_fn! {
fn single_const_generic<const N: usize>(_arg: ()) -> () {}
}
const_fn! {
fn single_type_generic<T>(_arg: ()) -> () {}
}
const_fn! {
fn single_type_generic_with_bound<T: Copy>(_arg: ()) -> () {}
}
const_fn! {
fn mixed_generics<const N: usize, T, U, const O: usize>(_arg: ()) -> () {}
}
const_fn! {
fn raw_arg(#[raw] _Arg: ()) -> () {
_Arg::ITEM
}
}
}