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,
72    PtxKernel,
75
76    AvrInterrupt,
78    AvrNonBlockingInterrupt,
79    Msp430Interrupt,
80    RiscvInterruptM,
81    RiscvInterruptS,
82    X86Interrupt,
83
84    Cdecl {
87        unwind: bool,
88    },
89    Stdcall {
91        unwind: bool,
92    },
93    Fastcall {
95        unwind: bool,
96    },
97    Thiscall {
99        unwind: bool,
100    },
101    Vectorcall {
103        unwind: bool,
104    },
105
106    SysV64 {
108        unwind: bool,
109    },
110    Win64 {
111        unwind: bool,
112    },
113}
114
115macro_rules! abi_impls {
116    ($e_name:ident = {
117        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
118    }) => {
119        impl $e_name {
120            pub const ALL_VARIANTS: &[Self] = &[
121                $($e_name::$variant $({ unwind: $uw })*,)*
122            ];
123            pub const fn as_str(&self) -> &'static str {
124                match self {
125                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
126                }
127            }
128        }
129
130        impl ::core::str::FromStr for $e_name {
131            type Err = AbiFromStrErr;
132            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
133                match s {
134                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
135                    _ => Err(AbiFromStrErr::Unknown),
136                }
137            }
138        }
139    }
140}
141
142abi_impls! {
143    ExternAbi = {
144            C { unwind: false } =><= "C",
145            C { unwind: true } =><= "C-unwind",
146            Rust =><= "Rust",
147            Aapcs { unwind: false } =><= "aapcs",
148            Aapcs { unwind: true } =><= "aapcs-unwind",
149            AvrInterrupt =><= "avr-interrupt",
150            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
151            Cdecl { unwind: false } =><= "cdecl",
152            Cdecl { unwind: true } =><= "cdecl-unwind",
153            CmseNonSecureCall =><= "cmse-nonsecure-call",
154            CmseNonSecureEntry =><= "cmse-nonsecure-entry",
155            Custom =><= "custom",
156            EfiApi =><= "efiapi",
157            Fastcall { unwind: false } =><= "fastcall",
158            Fastcall { unwind: true } =><= "fastcall-unwind",
159            GpuKernel =><= "gpu-kernel",
160            Msp430Interrupt =><= "msp430-interrupt",
161            PtxKernel =><= "ptx-kernel",
162            RiscvInterruptM =><= "riscv-interrupt-m",
163            RiscvInterruptS =><= "riscv-interrupt-s",
164            RustCall =><= "rust-call",
165            RustCold =><= "rust-cold",
166            RustInvalid =><= "rust-invalid",
167            Stdcall { unwind: false } =><= "stdcall",
168            Stdcall { unwind: true } =><= "stdcall-unwind",
169            System { unwind: false } =><= "system",
170            System { unwind: true } =><= "system-unwind",
171            SysV64 { unwind: false } =><= "sysv64",
172            SysV64 { unwind: true } =><= "sysv64-unwind",
173            Thiscall { unwind: false } =><= "thiscall",
174            Thiscall { unwind: true } =><= "thiscall-unwind",
175            Unadjusted =><= "unadjusted",
176            Vectorcall { unwind: false } =><= "vectorcall",
177            Vectorcall { unwind: true } =><= "vectorcall-unwind",
178            Win64 { unwind: false } =><= "win64",
179            Win64 { unwind: true } =><= "win64-unwind",
180            X86Interrupt =><= "x86-interrupt",
181    }
182}
183
184impl Ord for ExternAbi {
185    fn cmp(&self, rhs: &Self) -> Ordering {
186        self.as_str().cmp(rhs.as_str())
187    }
188}
189
190impl PartialOrd for ExternAbi {
191    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
192        Some(self.cmp(rhs))
193    }
194}
195
196impl PartialEq for ExternAbi {
197    fn eq(&self, rhs: &Self) -> bool {
198        self.cmp(rhs) == Ordering::Equal
199    }
200}
201
202impl Eq for ExternAbi {}
203
204impl Hash for ExternAbi {
205    fn hash<H: Hasher>(&self, state: &mut H) {
206        self.as_str().hash(state);
207        u32::from_be_bytes(*b"ABI\0").hash(state);
209    }
210}
211
212#[cfg(feature = "nightly")]
213impl<C> HashStable<C> for ExternAbi {
214    #[inline]
215    fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
216        Hash::hash(self, hasher);
217    }
218}
219
220#[cfg(feature = "nightly")]
221impl StableOrd for ExternAbi {
222    const CAN_USE_UNSTABLE_SORT: bool = true;
223
224    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
226}
227
228#[cfg(feature = "nightly")]
229rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
230
231#[cfg(feature = "nightly")]
232pub enum CVariadicStatus {
233    NotSupported,
234    Stable,
235    Unstable { feature: Symbol },
236}
237
238impl ExternAbi {
239    pub fn is_rustic_abi(self) -> bool {
246        use ExternAbi::*;
247        matches!(self, Rust | RustCall | RustCold)
248    }
249
250    #[cfg(feature = "nightly")]
254    pub fn supports_c_variadic(self) -> CVariadicStatus {
255        match self {
267            Self::C { .. }
268            | Self::Cdecl { .. }
269            | Self::Aapcs { .. }
270            | Self::Win64 { .. }
271            | Self::SysV64 { .. }
272            | Self::EfiApi => CVariadicStatus::Stable,
273            Self::System { .. } => {
274                CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs }
275            }
276            _ => CVariadicStatus::NotSupported,
277        }
278    }
279}
280
281pub fn all_names() -> Vec<&'static str> {
282    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
283}
284
285impl ExternAbi {
286    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
288
289    pub fn name(self) -> &'static str {
290        self.as_str()
291    }
292}
293
294impl fmt::Display for ExternAbi {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        write!(f, "\"{}\"", self.as_str())
297    }
298}