Skip to main content

core/intrinsics/
macros.rs

1//! Macros used to declare intrinsics.
2
3/// Declares an intrinsic that is generic over its type, but whose fallback body has to be
4/// written per-type. This is needed for the float intrinsics, which are generics but may
5/// have different fallback bodies for each float width.
6///
7/// ```ignore (illustrative)
8/// intrinsic_dispatch_on_type! {
9///     /// Returns the exponential of a float.
10///     #[rustc_nounwind]
11///     #[rustc_intrinsic]
12///     pub fn expf<T: bounds::FloatPrimitive>(x: T) -> T;
13///
14///     f16 => { expf(x as f32) as f16 }
15///     f32 => { libm::likely_available::expf(x) }
16///     f64 => { libm::likely_available::exp(x) }
17///     f128 => { libm::maybe_available::expf128(x) }
18/// }
19/// ```
20macro_rules! intrinsic_dispatch_on_type {
21    (
22        $(#[$attr:meta])*
23        $vis:vis fn $name:ident<$generic:ident: $bound:path>(
24            $($arg:ident: $arg_ty:ty),* $(,)?
25        ) -> $ret_ty:ty;
26        $($concrete:ty => $body:block)*
27    ) => {
28        mod $name {
29            use super::*;
30
31            pub trait Dispatch<$generic = Self>: $bound {
32                fn dispatch($($arg: $arg_ty),*) -> $ret_ty;
33            }
34
35            intrinsic_dispatch_on_type! {
36                @impls $generic, ($($arg: $arg_ty),*) -> $ret_ty,
37                $($concrete => $body)*
38            }
39        }
40
41        $(#[$attr])*
42        $vis fn $name<$generic: $name::Dispatch>($($arg: $arg_ty),*) -> $ret_ty {
43            <$generic as $name::Dispatch>::dispatch($($arg),*)
44        }
45    };
46
47    (
48        @impls $generic:ident, $args:tt -> $ret_ty:ty,
49        $($concrete:ty => $body:block)*
50    ) => {
51        $(intrinsic_dispatch_on_type! { @impl $generic = $concrete, $args -> $ret_ty $body })*
52    };
53
54    (
55        @impl $generic:ident = $concrete:ty,
56        ($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty $body:block
57    ) => {
58        // We need a const block here to define a type alias for the type var ($generic),
59        // because the arguments in `fn dispatch` are the ones used to define the intrinsic.
60        //
61        // For instance, when defining `fn exp<T: bounds::FloatPrimitive>(x: T) -> T`,
62        // the definition of `dispatch` below is going to be `fn dispatch(x: T) -> T`.
63        const _: () = {
64            type $generic = $concrete;
65
66            impl Dispatch<$concrete> for $concrete {
67                #[inline]
68                fn dispatch($($arg: $arg_ty),*) -> $ret_ty $body
69            }
70        };
71    };
72}
73
74pub(super) use intrinsic_dispatch_on_type;