use alloc::vec::Vec;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
#[cfg(feature = "nightly")]
use crate::rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher, StableOrd};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable};
#[cfg(feature = "nightly")]
use crate::rustc_span::Symbol;
use crate::rustc_abi::AbiFromStrErr;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
pub enum ExternAbi {
C {
unwind: bool,
},
System {
unwind: bool,
},
Rust,
RustCall,
RustCold,
RustInvalid,
RustPreserveNone,
RustTail,
LlvmIntrinsic,
Custom,
EfiApi,
Swift,
Aapcs {
unwind: bool,
},
CmseNonSecureCall,
CmseNonSecureEntry,
GpuKernel,
PtxKernel,
AvrInterrupt,
AvrNonBlockingInterrupt,
Msp430Interrupt,
RiscvInterruptM,
RiscvInterruptS,
X86Interrupt,
Cdecl {
unwind: bool,
},
Stdcall {
unwind: bool,
},
Fastcall {
unwind: bool,
},
Thiscall {
unwind: bool,
},
Vectorcall {
unwind: bool,
},
SysV64 {
unwind: bool,
},
Win64 {
unwind: bool,
},
}
macro_rules! abi_impls {
($e_name:ident = {
$($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
}) => {
impl $e_name {
pub const ALL_VARIANTS: &[Self] = &[
$($e_name::$variant $({ unwind: $uw })*,)*
];
pub const fn as_str(&self) -> &'static str {
match self {
$($e_name::$variant $( { unwind: $uw } )* => $tok,)*
}
}
const fn internal_const_eq(&self, other: &Self) -> bool {
match (self, other) {
$( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*
_ => false,
}
}
pub const fn as_packed(&self) -> u8 {
let mut index = 0;
while index < $e_name::ALL_VARIANTS.len() {
if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {
return index as u8;
}
index += 1;
}
panic!("unreachable: invalid ExternAbi variant");
}
pub const fn from_packed(index: u8) -> Self {
let index = index as usize;
assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");
$e_name::ALL_VARIANTS[index]
}
}
impl ::core::str::FromStr for $e_name {
type Err = AbiFromStrErr;
fn from_str(s: &str) -> Result<$e_name, Self::Err> {
match s {
$($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
_ => Err(AbiFromStrErr::Unknown),
}
}
}
}
}
abi_impls! {
ExternAbi = {
C { unwind: false } =><= "C",
C { unwind: true } =><= "C-unwind",
Rust =><= "Rust",
Swift =><= "Swift",
Aapcs { unwind: false } =><= "aapcs",
Aapcs { unwind: true } =><= "aapcs-unwind",
AvrInterrupt =><= "avr-interrupt",
AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
Cdecl { unwind: false } =><= "cdecl",
Cdecl { unwind: true } =><= "cdecl-unwind",
CmseNonSecureCall =><= "cmse-nonsecure-call",
CmseNonSecureEntry =><= "cmse-nonsecure-entry",
Custom =><= "custom",
EfiApi =><= "efiapi",
Fastcall { unwind: false } =><= "fastcall",
Fastcall { unwind: true } =><= "fastcall-unwind",
GpuKernel =><= "gpu-kernel",
LlvmIntrinsic =><= "llvm-intrinsic",
Msp430Interrupt =><= "msp430-interrupt",
PtxKernel =><= "ptx-kernel",
RiscvInterruptM =><= "riscv-interrupt-m",
RiscvInterruptS =><= "riscv-interrupt-s",
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustInvalid =><= "rust-invalid",
RustPreserveNone =><= "rust-preserve-none",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
System { unwind: true } =><= "system-unwind",
SysV64 { unwind: false } =><= "sysv64",
SysV64 { unwind: true } =><= "sysv64-unwind",
RustTail =><= "tail",
Thiscall { unwind: false } =><= "thiscall",
Thiscall { unwind: true } =><= "thiscall-unwind",
Vectorcall { unwind: false } =><= "vectorcall",
Vectorcall { unwind: true } =><= "vectorcall-unwind",
Win64 { unwind: false } =><= "win64",
Win64 { unwind: true } =><= "win64-unwind",
X86Interrupt =><= "x86-interrupt",
}
}
impl Ord for ExternAbi {
fn cmp(&self, rhs: &Self) -> Ordering {
self.as_str().cmp(rhs.as_str())
}
}
impl PartialOrd for ExternAbi {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl PartialEq for ExternAbi {
fn eq(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Equal
}
}
impl Eq for ExternAbi {}
impl Hash for ExternAbi {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
// double-assurance of a prefix breaker
u32::from_be_bytes(*b"ABI\0").hash(state);
}
}
#[cfg(feature = "nightly")]
impl StableHash for ExternAbi {
#[inline]
fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {
Hash::hash(self, hasher);
}
}
#[cfg(feature = "nightly")]
impl StableOrd for ExternAbi {
const CAN_USE_UNSTABLE_SORT: bool = true;
// because each ABI is hashed like a string, there is no possible instability
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
#[cfg(feature = "nightly")]
crate::into_diag_arg_using_display!(ExternAbi);
#[cfg(feature = "nightly")]
#[derive(Debug)]
pub enum CVariadicStatus {
NotSupported,
Stable,
Unstable { feature: Symbol },
}
impl ExternAbi {
/// An ABI "like Rust"
///
/// These ABIs are fully controlled by the Rust compiler, which means they
/// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
/// - often diverge from the C ABI
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail)
}
/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
/// of such functions via `extern` blocks and definition via naked functions; there's a
/// separate check during AST construction guarding *definitions* of variadic functions.
#[cfg(feature = "nightly")]
pub fn supports_c_variadic(self) -> CVariadicStatus {
// * C and Cdecl obviously support varargs.
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
// * EfiApi is based on Win64 or C, so it also supports it.
// * System automatically falls back to C when used with variadics, therefore supports it.
//
// * Stdcall does not, because it would be impossible for the callee to clean
// up the arguments. (callee doesn't know how many arguments are there)
// * Same for Fastcall, Vectorcall and Thiscall.
// * Other calling conventions are related to hardware or the compiler itself.
//
// All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
match self {
Self::C { .. }
| Self::Cdecl { .. }
| Self::Aapcs { .. }
| Self::Win64 { .. }
| Self::SysV64 { .. }
| Self::EfiApi
| Self::System { .. } => CVariadicStatus::Stable,
_ => CVariadicStatus::NotSupported,
}
}
/// Returns whether the ABI supports guaranteed tail calls.
#[cfg(feature = "nightly")]
pub fn supports_guaranteed_tail_call(self) -> bool {
match self {
Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
// See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
// before returning, and hence cannot guarantee a tail call.
false
}
Self::AvrInterrupt
| Self::AvrNonBlockingInterrupt
| Self::Msp430Interrupt
| Self::RiscvInterruptM
| Self::RiscvInterruptS
| Self::X86Interrupt => {
// See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
false
}
Self::GpuKernel | Self::PtxKernel => {
// See https://godbolt.org/z/jq5TE5jK1.
false
}
Self::Custom => {
// This ABI does not support calls at all (except via assembly).
false
}
Self::RustCall => {
// Argument untupling requires additional support for tail calls to be possible,
// see <https://github.com/rust-lang/rust/pull/158248>. There is no real uses of
// tail calling `extern "rust-call"` functions
false
}
Self::C { .. }
| Self::System { .. }
| Self::Rust
| Self::RustCold
| Self::RustInvalid
| Self::LlvmIntrinsic
| Self::EfiApi
| Self::Aapcs { .. }
| Self::Cdecl { .. }
| Self::Stdcall { .. }
| Self::Fastcall { .. }
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. }
| Self::RustPreserveNone
| Self::RustTail
| Self::Swift => true,
}
}
}
pub fn all_names() -> Vec<&'static str> {
ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
}
impl ExternAbi {
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
pub fn name(self) -> &'static str {
self.as_str()
}
}
impl fmt::Display for ExternAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.as_str())
}
}