fn_ptr/
abi.rs

1#[cfg(nightly_build)]
2use core::marker::ConstParamTy;
3use core::{
4    fmt::{Debug, Display},
5    str::FromStr,
6};
7
8use const_panic::concat_panic;
9
10/// The abi or calling convention of a function pointer.
11#[repr(u8)]
12#[cfg_attr(nightly_build, derive(ConstParamTy))]
13#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14pub enum Abi {
15    /// The default ABI when you write a normal `fn foo()` in any Rust code.
16    Rust = 0,
17    /// This is the same as `extern fn foo()`; whatever the default your C compiler supports.
18    C = 1,
19    /// Usually the same as [`extern "C"`](Abi::C), except on Win32, in which case it's [`"stdcall"`](Abi::Stdcall), or what you should use to link to the Windows API itself.
20    System = 2,
21    /// The default for C code on `x86_64` Windows.
22    Win64 = 3,
23    /// The default for C code on non-Windows `x86_64`.
24    Sysv64 = 4,
25    /// The default for ARM.
26    Aapcs = 5,
27    /// The default for `x86_32` C code.
28    Cdecl = 6,
29    /// The default for the Win32 API on `x86_32`.
30    Stdcall = 7,
31    /// The `fastcall` ABI -- corresponds to MSVC's `__fastcall` and GCC and clang's `__attribute__((fastcall))`
32    Fastcall = 8,
33    /// The `vectorcall` ABI -- corresponds to MSVC's `__vectorcall` and GCC and clang's `__attribute__((vectorcall))`
34    Vectorcall = 9,
35}
36
37impl Abi {
38    /// Returns the string representation of this ABI.
39    #[must_use]
40    pub const fn to_str(&self) -> &'static str {
41        match self {
42            Abi::Rust => "Rust",
43            Abi::C => "C",
44            Abi::System => "system",
45            Abi::Win64 => "win64",
46            Abi::Sysv64 => "sysv64",
47            Abi::Aapcs => "aapcs",
48            Abi::Cdecl => "cdecl",
49            Abi::Stdcall => "stdcall",
50            Abi::Fastcall => "fastcall",
51            Abi::Vectorcall => "vectorcall",
52        }
53    }
54}
55
56impl Abi {
57    /// Returns true if this ABI is an alias.
58    #[must_use]
59    pub fn is_alias(&self) -> bool {
60        matches!(self, Abi::C | Abi::System)
61    }
62
63    /// Returns true if this ABI is a concrete ABI, not an alias.
64    #[must_use]
65    pub fn is_concrete(&self) -> bool {
66        !self.is_alias()
67    }
68
69    /// Returns the concrete ABI for this ABI on the current target.
70    #[must_use]
71    pub fn concrete(&self) -> Abi {
72        match self {
73            Abi::C => {
74                // "C" maps to platform default for C
75                if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
76                    Abi::Win64
77                } else if cfg!(target_arch = "x86_64") {
78                    Abi::Sysv64
79                } else if cfg!(target_arch = "x86") {
80                    Abi::Cdecl
81                } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
82                    Abi::Aapcs
83                } else {
84                    Abi::Cdecl // fallback
85                }
86            }
87            Abi::System => {
88                if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
89                    Abi::Win64
90                } else if cfg!(target_os = "windows") && cfg!(target_arch = "x86") {
91                    Abi::Stdcall
92                } else if cfg!(target_arch = "x86_64") {
93                    Abi::Sysv64
94                } else if cfg!(target_arch = "x86") {
95                    Abi::Cdecl
96                } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
97                    Abi::Aapcs
98                } else {
99                    Abi::Cdecl // fallback
100                }
101            }
102            other => *other,
103        }
104    }
105}
106
107impl FromStr for Abi {
108    type Err = ();
109
110    fn from_str(s: &str) -> Result<Self, Self::Err> {
111        match s {
112            "" | "Rust" => Ok(Abi::Rust),
113            "C" => Ok(Abi::C),
114            "system" => Ok(Abi::System),
115            "win64" => Ok(Abi::Win64),
116            "sysv64" => Ok(Abi::Sysv64),
117            "aapcs" => Ok(Abi::Aapcs),
118            "cdecl" => Ok(Abi::Cdecl),
119            "stdcall" => Ok(Abi::Stdcall),
120            "fastcall" => Ok(Abi::Fastcall),
121            "vectorcall" => Ok(Abi::Vectorcall),
122            _ => Err(()),
123        }
124    }
125}
126
127impl Display for Abi {
128    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
129        write!(f, "{}", self.to_str())
130    }
131}
132
133/// Parse a string into an [`Abi`] if known, otherwise returns `None`.
134#[must_use]
135pub const fn parse(conv: &'static str) -> Option<Abi> {
136    if konst::eq_str(conv, "") || konst::eq_str(conv, "Rust") {
137        Some(Abi::Rust)
138    } else if konst::eq_str(conv, "C") {
139        Some(Abi::C)
140    } else if konst::eq_str(conv, "system") {
141        Some(Abi::System)
142    } else if konst::eq_str(conv, "win64") {
143        Some(Abi::Win64)
144    } else if konst::eq_str(conv, "sysv64") {
145        Some(Abi::Sysv64)
146    } else if konst::eq_str(conv, "aapcs") {
147        Some(Abi::Aapcs)
148    } else if konst::eq_str(conv, "cdecl") {
149        Some(Abi::Cdecl)
150    } else if konst::eq_str(conv, "stdcall") {
151        Some(Abi::Stdcall)
152    } else if konst::eq_str(conv, "fastcall") {
153        Some(Abi::Fastcall)
154    } else if konst::eq_str(conv, "vectorcall") {
155        Some(Abi::Vectorcall)
156    } else {
157        None
158    }
159}
160
161/// Parse a string into an [`Abi`] and panic if unknown.
162#[must_use]
163pub const fn parse_or_fail(conv: &'static str) -> Abi {
164    if let Some(c) = parse(conv) {
165        c
166    } else {
167        concat_panic!("invalid or unknown abi", conv)
168    }
169}
170
171#[cfg(nightly_build)]
172pub(crate) type AbiKey = Abi;
173#[cfg(not(nightly_build))]
174pub(crate) type AbiKey = u8;
175
176/// Returns the value used to designate the given ABI in const generics.
177/// For stable or beta builds this returns an [`u8`], while on nightly the [`Abi`] instance is returned.
178#[must_use]
179pub const fn key(abi: Abi) -> AbiKey {
180    abi as _
181}
182
183/// Converts an ABI string like "C" into the corresponding value for use in const generics.
184/// This is most useful for stable rust since there [`u8`]s are used.
185///
186/// # Example
187///
188/// ```rust
189/// # use fn_ptr::make_non_extern;
190/// type F = extern "C" fn(i32) -> i32;
191/// type R = make_non_extern!(F);
192/// // `R` is `fn(i32) -> i32`
193/// ```
194///
195/// Equivalent to:
196/// ```rust
197/// # use fn_ptr::{Abi, with_abi};
198/// # type F = extern "C" fn(i32) -> i32;
199/// # type G =
200/// with_abi!(Abi::Rust, F)
201/// # ;
202/// ```
203#[macro_export]
204macro_rules! abi {
205    ( $abi:literal ) => {
206        $crate::abi::key($crate::abi::parse_or_fail($abi))
207    };
208}