1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5#[cfg(feature = "nightly")]
6use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
7#[cfg(feature = "nightly")]
8use rustc_macros::{Decodable, Encodable};
9#[cfg(feature = "nightly")]
10use rustc_span::Symbol;
11
12use crate::AbiFromStrErr;
13
14#[cfg(test)]
15mod tests;
16
17#[derive(Clone, Copy, Debug)]
19#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
20pub enum ExternAbi {
21 C {
24 unwind: bool,
25 },
26 System {
28 unwind: bool,
29 },
30
31 Rust,
33 RustCall,
36 RustCold,
40
41 RustInvalid,
44
45 Unadjusted,
48
49 Custom,
53
54 EfiApi,
57
58 Aapcs {
61 unwind: bool,
62 },
63 CmseNonSecureCall,
65 CmseNonSecureEntry,
67
68 GpuKernel,
71 PtxKernel,
74
75 AvrInterrupt,
77 AvrNonBlockingInterrupt,
78 Msp430Interrupt,
79 RiscvInterruptM,
80 RiscvInterruptS,
81 X86Interrupt,
82
83 Cdecl {
86 unwind: bool,
87 },
88 Stdcall {
90 unwind: bool,
91 },
92 Fastcall {
94 unwind: bool,
95 },
96 Thiscall {
98 unwind: bool,
99 },
100 Vectorcall {
102 unwind: bool,
103 },
104
105 SysV64 {
107 unwind: bool,
108 },
109 Win64 {
110 unwind: bool,
111 },
112}
113
114macro_rules! abi_impls {
115 ($e_name:ident = {
116 $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
117 }) => {
118 impl $e_name {
119 pub const ALL_VARIANTS: &[Self] = &[
120 $($e_name::$variant $({ unwind: $uw })*,)*
121 ];
122 pub const fn as_str(&self) -> &'static str {
123 match self {
124 $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
125 }
126 }
127 }
128
129 impl ::core::str::FromStr for $e_name {
130 type Err = AbiFromStrErr;
131 fn from_str(s: &str) -> Result<$e_name, Self::Err> {
132 match s {
133 $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
134 _ => Err(AbiFromStrErr::Unknown),
135 }
136 }
137 }
138 }
139}
140
141abi_impls! {
142 ExternAbi = {
143 C { unwind: false } =><= "C",
144 C { unwind: true } =><= "C-unwind",
145 Rust =><= "Rust",
146 Aapcs { unwind: false } =><= "aapcs",
147 Aapcs { unwind: true } =><= "aapcs-unwind",
148 AvrInterrupt =><= "avr-interrupt",
149 AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
150 Cdecl { unwind: false } =><= "cdecl",
151 Cdecl { unwind: true } =><= "cdecl-unwind",
152 CmseNonSecureCall =><= "cmse-nonsecure-call",
153 CmseNonSecureEntry =><= "cmse-nonsecure-entry",
154 Custom =><= "custom",
155 EfiApi =><= "efiapi",
156 Fastcall { unwind: false } =><= "fastcall",
157 Fastcall { unwind: true } =><= "fastcall-unwind",
158 GpuKernel =><= "gpu-kernel",
159 Msp430Interrupt =><= "msp430-interrupt",
160 PtxKernel =><= "ptx-kernel",
161 RiscvInterruptM =><= "riscv-interrupt-m",
162 RiscvInterruptS =><= "riscv-interrupt-s",
163 RustCall =><= "rust-call",
164 RustCold =><= "rust-cold",
165 RustInvalid =><= "rust-invalid",
166 Stdcall { unwind: false } =><= "stdcall",
167 Stdcall { unwind: true } =><= "stdcall-unwind",
168 System { unwind: false } =><= "system",
169 System { unwind: true } =><= "system-unwind",
170 SysV64 { unwind: false } =><= "sysv64",
171 SysV64 { unwind: true } =><= "sysv64-unwind",
172 Thiscall { unwind: false } =><= "thiscall",
173 Thiscall { unwind: true } =><= "thiscall-unwind",
174 Unadjusted =><= "unadjusted",
175 Vectorcall { unwind: false } =><= "vectorcall",
176 Vectorcall { unwind: true } =><= "vectorcall-unwind",
177 Win64 { unwind: false } =><= "win64",
178 Win64 { unwind: true } =><= "win64-unwind",
179 X86Interrupt =><= "x86-interrupt",
180 }
181}
182
183impl Ord for ExternAbi {
184 fn cmp(&self, rhs: &Self) -> Ordering {
185 self.as_str().cmp(rhs.as_str())
186 }
187}
188
189impl PartialOrd for ExternAbi {
190 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
191 Some(self.cmp(rhs))
192 }
193}
194
195impl PartialEq for ExternAbi {
196 fn eq(&self, rhs: &Self) -> bool {
197 self.cmp(rhs) == Ordering::Equal
198 }
199}
200
201impl Eq for ExternAbi {}
202
203impl Hash for ExternAbi {
204 fn hash<H: Hasher>(&self, state: &mut H) {
205 self.as_str().hash(state);
206 u32::from_be_bytes(*b"ABI\0").hash(state);
208 }
209}
210
211#[cfg(feature = "nightly")]
212impl<C> HashStable<C> for ExternAbi {
213 #[inline]
214 fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
215 Hash::hash(self, hasher);
216 }
217}
218
219#[cfg(feature = "nightly")]
220impl StableOrd for ExternAbi {
221 const CAN_USE_UNSTABLE_SORT: bool = true;
222
223 const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
225}
226
227#[cfg(feature = "nightly")]
228rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
229
230#[cfg(feature = "nightly")]
231pub enum CVariadicStatus {
232 NotSupported,
233 Stable,
234 Unstable { feature: Symbol },
235}
236
237impl ExternAbi {
238 pub fn is_rustic_abi(self) -> bool {
245 use ExternAbi::*;
246 matches!(self, Rust | RustCall | RustCold)
247 }
248
249 #[cfg(feature = "nightly")]
253 pub fn supports_c_variadic(self) -> CVariadicStatus {
254 match self {
266 Self::C { .. }
267 | Self::Cdecl { .. }
268 | Self::Aapcs { .. }
269 | Self::Win64 { .. }
270 | Self::SysV64 { .. }
271 | Self::EfiApi
272 | Self::System { .. } => CVariadicStatus::Stable,
273 _ => CVariadicStatus::NotSupported,
274 }
275 }
276
277 #[cfg(feature = "nightly")]
279 pub fn supports_guaranteed_tail_call(self) -> bool {
280 match self {
281 Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
282 false
285 }
286 Self::AvrInterrupt
287 | Self::AvrNonBlockingInterrupt
288 | Self::Msp430Interrupt
289 | Self::RiscvInterruptM
290 | Self::RiscvInterruptS
291 | Self::X86Interrupt => {
292 false
294 }
295 Self::GpuKernel | Self::PtxKernel => {
296 false
298 }
299 Self::Custom => {
300 false
302 }
303 Self::C { .. }
304 | Self::System { .. }
305 | Self::Rust
306 | Self::RustCall
307 | Self::RustCold
308 | Self::RustInvalid
309 | Self::Unadjusted
310 | Self::EfiApi
311 | Self::Aapcs { .. }
312 | Self::Cdecl { .. }
313 | Self::Stdcall { .. }
314 | Self::Fastcall { .. }
315 | Self::Thiscall { .. }
316 | Self::Vectorcall { .. }
317 | Self::SysV64 { .. }
318 | Self::Win64 { .. } => true,
319 }
320 }
321}
322
323pub fn all_names() -> Vec<&'static str> {
324 ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
325}
326
327impl ExternAbi {
328 pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
330
331 pub fn name(self) -> &'static str {
332 self.as_str()
333 }
334}
335
336impl fmt::Display for ExternAbi {
337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338 write!(f, "\"{}\"", self.as_str())
339 }
340}