fn_ptr/
abi.rs

1#[cfg(feature = "nightly")]
2use core::marker::ConstParamTy;
3use core::{
4    fmt::{Debug, Display},
5    str::FromStr,
6};
7
8use const_panic::concat_panic;
9
10#[cfg_attr(feature = "nightly", derive(ConstParamTy))]
11#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
12/// The abi or calling convention of a function pointer.
13pub enum Abi {
14    /// The default ABI when you write a normal `fn foo()` in any Rust code.
15    Rust,
16    /// This is the same as `extern fn foo()`; whatever the default your C compiler supports.
17    C,
18    /// 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.
19    System,
20    /// The default for C code on x86_64 Windows.
21    Win64,
22    /// The default for C code on non-Windows x86_64.
23    Sysv64,
24    /// The default for ARM.
25    Aapcs,
26    /// The default for x86_32 C code.
27    Cdecl,
28    /// The default for the Win32 API on x86_32.
29    Stdcall,
30    /// The `fastcall` ABI -- corresponds to MSVC's `__fastcall` and GCC and clang's `__attribute__((fastcall))`
31    Fastcall,
32    /// The `vectorcall` ABI -- corresponds to MSVC's `__vectorcall` and GCC and clang's `__attribute__((vectorcall))`
33    Vectorcall,
34}
35
36impl Abi {
37    /// Returns the string representation of this ABI.
38    #[must_use]
39    pub const fn to_str(&self) -> &'static str {
40        match self {
41            Abi::Rust => "Rust",
42            Abi::C => "C",
43            Abi::System => "system",
44            Abi::Win64 => "win64",
45            Abi::Sysv64 => "sysv64",
46            Abi::Aapcs => "aapcs",
47            Abi::Cdecl => "cdecl",
48            Abi::Stdcall => "stdcall",
49            Abi::Fastcall => "fastcall",
50            Abi::Vectorcall => "vectorcall",
51        }
52    }
53}
54
55impl Abi {
56    /// Returns true if this ABI is an alias.
57    pub fn is_alias(&self) -> bool {
58        matches!(self, Abi::C | Abi::System)
59    }
60
61    /// Returns true if this ABI is a concrete ABI, not an alias.
62    pub fn is_concrete(&self) -> bool {
63        !self.is_alias()
64    }
65
66    /// Returns the concrete ABI for this ABI on the current target.
67    pub fn concrete(&self) -> Abi {
68        match self {
69            Abi::C => {
70                // "C" maps to platform default for C
71                if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
72                    Abi::Win64
73                } else if cfg!(target_arch = "x86_64") {
74                    Abi::Sysv64
75                } else if cfg!(target_arch = "x86") {
76                    Abi::Cdecl
77                } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
78                    Abi::Aapcs
79                } else {
80                    Abi::Cdecl // fallback
81                }
82            }
83            Abi::System => {
84                if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
85                    Abi::Win64
86                } else if cfg!(target_os = "windows") && cfg!(target_arch = "x86") {
87                    Abi::Stdcall
88                } else if cfg!(target_arch = "x86_64") {
89                    Abi::Sysv64
90                } else if cfg!(target_arch = "x86") {
91                    Abi::Cdecl
92                } else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
93                    Abi::Aapcs
94                } else {
95                    Abi::Cdecl // fallback
96                }
97            }
98            other => *other,
99        }
100    }
101}
102
103impl FromStr for Abi {
104    type Err = ();
105
106    fn from_str(s: &str) -> Result<Self, Self::Err> {
107        match s {
108            "" | "Rust" => Ok(Abi::Rust),
109            "C" => Ok(Abi::C),
110            "system" => Ok(Abi::System),
111            "win64" => Ok(Abi::Win64),
112            "sysv64" => Ok(Abi::Sysv64),
113            "aapcs" => Ok(Abi::Aapcs),
114            "cdecl" => Ok(Abi::Cdecl),
115            "stdcall" => Ok(Abi::Stdcall),
116            "fastcall" => Ok(Abi::Fastcall),
117            "vectorcall" => Ok(Abi::Vectorcall),
118            _ => Err(()),
119        }
120    }
121}
122
123impl Display for Abi {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        write!(f, "{}", self.to_str())
126    }
127}
128
129/// Parse a string into an [`Abi`] if known, otherwise returns `None`.
130#[must_use]
131pub const fn parse(conv: &'static str) -> Option<Abi> {
132    if konst::eq_str(conv, "") || konst::eq_str(conv, "Rust") {
133        Some(Abi::Rust)
134    } else if konst::eq_str(conv, "C") {
135        Some(Abi::C)
136    } else if konst::eq_str(conv, "system") {
137        Some(Abi::System)
138    } else if konst::eq_str(conv, "win64") {
139        Some(Abi::Win64)
140    } else if konst::eq_str(conv, "sysv64") {
141        Some(Abi::Sysv64)
142    } else if konst::eq_str(conv, "aapcs") {
143        Some(Abi::Aapcs)
144    } else if konst::eq_str(conv, "cdecl") {
145        Some(Abi::Cdecl)
146    } else if konst::eq_str(conv, "stdcall") {
147        Some(Abi::Stdcall)
148    } else if konst::eq_str(conv, "fastcall") {
149        Some(Abi::Fastcall)
150    } else if konst::eq_str(conv, "vectorcall") {
151        Some(Abi::Vectorcall)
152    } else {
153        None
154    }
155}
156
157/// Parse a string into an [`Abi`] and panic if unknown.
158#[must_use]
159pub const fn parse_or_fail(conv: &'static str) -> Abi {
160    match parse(conv) {
161        Some(c) => c,
162        None => concat_panic!("invalid or unknown abi", conv),
163    }
164}