compiler/rustc_abi/src/extern_abi.rs RUST 392 lines View on github.com → Search inside
1use std::cmp::Ordering;2use std::fmt;3use std::hash::{Hash, Hasher};45#[cfg(feature = "nightly")]6use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher, StableOrd};7#[cfg(feature = "nightly")]8use rustc_macros::{Decodable, Encodable};9#[cfg(feature = "nightly")]10use rustc_span::Symbol;1112use crate::AbiFromStrErr;1314#[cfg(test)]15mod tests;1617/// ABI we expect to see within `extern "{abi}"`18#[derive(Clone, Copy, Debug)]19#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]20pub enum ExternAbi {21    /* universal */22    /// presumed C ABI for the platform23    C {24        unwind: bool,25    },26    /// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"27    System {28        unwind: bool,29    },3031    /// that's us!32    Rust,33    /// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone34    /// puts in the work to make it viable again... but would we need a special ABI?35    RustCall,36    /// For things unlikely to be called, where reducing register pressure in37    /// `extern "Rust"` callers is worth paying extra cost in the callee.38    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.39    RustCold,4041    /// An always-invalid ABI that's used to test "this ABI is not supported by this platform"42    /// in a platform-agnostic way.43    RustInvalid,4445    /// Preserves no registers.46    ///47    /// Note, that this ABI is not stable in the registers it uses, is intended as an optimization48    /// and may fall-back to a more conservative calling convention if the backend does not support49    /// forcing callers to save all registers.50    RustPreserveNone,5152    /// Ensures that calls in tail position can always be optimized into a jump.53    ///54    /// This ABI is not stable, and relies on LLVM implementation details.55    RustTail,5657    /// Unstable ABI used to call LLVM intrinsics.58    LlvmIntrinsic,5960    /// An ABI that rustc does not know how to call or define. Functions with this ABI can61    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only62    /// be called from inline assembly.63    Custom,6465    /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias66    /// and only valid on platforms that have a UEFI standard67    EfiApi,6869    /// Swift's calling convention, used to interoperate with Swift code without70    /// going through C as an intermediary.71    Swift,7273    /* arm */74    /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this75    Aapcs {76        unwind: bool,77    },78    /// extremely constrained barely-C ABI for TrustZone79    CmseNonSecureCall,80    /// extremely constrained barely-C ABI for TrustZone81    CmseNonSecureEntry,8283    /* gpu */84    /// An entry-point function called by the GPU's host85    GpuKernel,86    /// An entry-point function called by the GPU's host87    // FIXME: why do we have two of these?88    PtxKernel,8990    /* interrupt */91    AvrInterrupt,92    AvrNonBlockingInterrupt,93    Msp430Interrupt,94    RiscvInterruptM,95    RiscvInterruptS,96    X86Interrupt,9798    /* x86 */99    /// `ExternAbi::C` but spelled funny because x86100    Cdecl {101        unwind: bool,102    },103    /// gnu-stdcall on "unix" and win-stdcall on "windows"104    Stdcall {105        unwind: bool,106    },107    /// gnu-fastcall on "unix" and win-fastcall on "windows"108    Fastcall {109        unwind: bool,110    },111    /// windows C++ ABI112    Thiscall {113        unwind: bool,114    },115    /// uses AVX and stuff116    Vectorcall {117        unwind: bool,118    },119120    /* x86_64 */121    SysV64 {122        unwind: bool,123    },124    Win64 {125        unwind: bool,126    },127}128129macro_rules! abi_impls {130    ($e_name:ident = {131        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*132    }) => {133        impl $e_name {134            pub const ALL_VARIANTS: &[Self] = &[135                $($e_name::$variant $({ unwind: $uw })*,)*136            ];137            pub const fn as_str(&self) -> &'static str {138                match self {139                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*140                }141            }142            // FIXME(FnSigKind): when PartialEq is stably const, use it instead143            const fn internal_const_eq(&self, other: &Self) -> bool {144                match (self, other) {145                    $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*146                    _ => false,147                }148            }149            // ALL_VARIANTS.iter().position(|v| v == self), but const150            pub const fn as_packed(&self) -> u8 {151                let mut index = 0;152                while index < $e_name::ALL_VARIANTS.len() {153                    if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {154                        return index as u8;155                    }156                    index += 1;157                }158                panic!("unreachable: invalid ExternAbi variant");159            }160            pub const fn from_packed(index: u8) -> Self {161                let index = index as usize;162                assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");163                $e_name::ALL_VARIANTS[index]164            }165        }166167        impl ::core::str::FromStr for $e_name {168            type Err = AbiFromStrErr;169            fn from_str(s: &str) -> Result<$e_name, Self::Err> {170                match s {171                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*172                    _ => Err(AbiFromStrErr::Unknown),173                }174            }175        }176    }177}178179abi_impls! {180    ExternAbi = {181            C { unwind: false } =><= "C",182            C { unwind: true } =><= "C-unwind",183            Rust =><= "Rust",184            Swift =><= "Swift",185            Aapcs { unwind: false } =><= "aapcs",186            Aapcs { unwind: true } =><= "aapcs-unwind",187            AvrInterrupt =><= "avr-interrupt",188            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",189            Cdecl { unwind: false } =><= "cdecl",190            Cdecl { unwind: true } =><= "cdecl-unwind",191            CmseNonSecureCall =><= "cmse-nonsecure-call",192            CmseNonSecureEntry =><= "cmse-nonsecure-entry",193            Custom =><= "custom",194            EfiApi =><= "efiapi",195            Fastcall { unwind: false } =><= "fastcall",196            Fastcall { unwind: true } =><= "fastcall-unwind",197            GpuKernel =><= "gpu-kernel",198            LlvmIntrinsic =><= "llvm-intrinsic",199            Msp430Interrupt =><= "msp430-interrupt",200            PtxKernel =><= "ptx-kernel",201            RiscvInterruptM =><= "riscv-interrupt-m",202            RiscvInterruptS =><= "riscv-interrupt-s",203            RustCall =><= "rust-call",204            RustCold =><= "rust-cold",205            RustInvalid =><= "rust-invalid",206            RustPreserveNone =><= "rust-preserve-none",207            Stdcall { unwind: false } =><= "stdcall",208            Stdcall { unwind: true } =><= "stdcall-unwind",209            System { unwind: false } =><= "system",210            System { unwind: true } =><= "system-unwind",211            SysV64 { unwind: false } =><= "sysv64",212            SysV64 { unwind: true } =><= "sysv64-unwind",213            RustTail =><= "tail",214            Thiscall { unwind: false } =><= "thiscall",215            Thiscall { unwind: true } =><= "thiscall-unwind",216            Vectorcall { unwind: false } =><= "vectorcall",217            Vectorcall { unwind: true } =><= "vectorcall-unwind",218            Win64 { unwind: false } =><= "win64",219            Win64 { unwind: true } =><= "win64-unwind",220            X86Interrupt =><= "x86-interrupt",221    }222}223224impl Ord for ExternAbi {225    fn cmp(&self, rhs: &Self) -> Ordering {226        self.as_str().cmp(rhs.as_str())227    }228}229230impl PartialOrd for ExternAbi {231    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {232        Some(self.cmp(rhs))233    }234}235236impl PartialEq for ExternAbi {237    fn eq(&self, rhs: &Self) -> bool {238        self.cmp(rhs) == Ordering::Equal239    }240}241242impl Eq for ExternAbi {}243244impl Hash for ExternAbi {245    fn hash<H: Hasher>(&self, state: &mut H) {246        self.as_str().hash(state);247        // double-assurance of a prefix breaker248        u32::from_be_bytes(*b"ABI\0").hash(state);249    }250}251252#[cfg(feature = "nightly")]253impl StableHash for ExternAbi {254    #[inline]255    fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {256        Hash::hash(self, hasher);257    }258}259260#[cfg(feature = "nightly")]261impl StableOrd for ExternAbi {262    const CAN_USE_UNSTABLE_SORT: bool = true;263264    // because each ABI is hashed like a string, there is no possible instability265    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();266}267268#[cfg(feature = "nightly")]269rustc_error_messages::into_diag_arg_using_display!(ExternAbi);270271#[cfg(feature = "nightly")]272#[derive(Debug)]273pub enum CVariadicStatus {274    NotSupported,275    Stable,276    Unstable { feature: Symbol },277}278279impl ExternAbi {280    /// An ABI "like Rust"281    ///282    /// These ABIs are fully controlled by the Rust compiler, which means they283    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`284    /// - often diverge from the C ABI285    /// - are subject to change between compiler versions286    pub fn is_rustic_abi(self) -> bool {287        use ExternAbi::*;288        matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail)289    }290291    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*292    /// of such functions via `extern` blocks and definition via naked functions; there's a293    /// separate check during AST construction guarding *definitions* of variadic functions.294    #[cfg(feature = "nightly")]295    pub fn supports_c_variadic(self) -> CVariadicStatus {296        // * C and Cdecl obviously support varargs.297        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.298        // * EfiApi is based on Win64 or C, so it also supports it.299        // * System automatically falls back to C when used with variadics, therefore supports it.300        //301        // * Stdcall does not, because it would be impossible for the callee to clean302        //   up the arguments. (callee doesn't know how many arguments are there)303        // * Same for Fastcall, Vectorcall and Thiscall.304        // * Other calling conventions are related to hardware or the compiler itself.305        //306        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.307        match self {308            Self::C { .. }309            | Self::Cdecl { .. }310            | Self::Aapcs { .. }311            | Self::Win64 { .. }312            | Self::SysV64 { .. }313            | Self::EfiApi314            | Self::System { .. } => CVariadicStatus::Stable,315            _ => CVariadicStatus::NotSupported,316        }317    }318319    /// Returns whether the ABI supports guaranteed tail calls.320    #[cfg(feature = "nightly")]321    pub fn supports_guaranteed_tail_call(self) -> bool {322        match self {323            Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {324                // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers325                // before returning, and hence cannot guarantee a tail call.326                false327            }328            Self::AvrInterrupt329            | Self::AvrNonBlockingInterrupt330            | Self::Msp430Interrupt331            | Self::RiscvInterruptM332            | Self::RiscvInterruptS333            | Self::X86Interrupt => {334                // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.335                false336            }337            Self::GpuKernel | Self::PtxKernel => {338                // See https://godbolt.org/z/jq5TE5jK1.339                false340            }341            Self::Custom => {342                // This ABI does not support calls at all (except via assembly).343                false344            }345            Self::RustCall => {346                // Argument untupling requires additional support for tail calls to be possible,347                // see <https://github.com/rust-lang/rust/pull/158248>. There is no real uses of348                // tail calling `extern "rust-call"` functions349                false350            }351352            Self::C { .. }353            | Self::System { .. }354            | Self::Rust355            | Self::RustCold356            | Self::RustInvalid357            | Self::LlvmIntrinsic358            | Self::EfiApi359            | Self::Aapcs { .. }360            | Self::Cdecl { .. }361            | Self::Stdcall { .. }362            | Self::Fastcall { .. }363            | Self::Thiscall { .. }364            | Self::Vectorcall { .. }365            | Self::SysV64 { .. }366            | Self::Win64 { .. }367            | Self::RustPreserveNone368            | Self::RustTail369            | Self::Swift => true,370        }371    }372}373374pub fn all_names() -> Vec<&'static str> {375    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()376}377378impl ExternAbi {379    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.380    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };381382    pub fn name(self) -> &'static str {383        self.as_str()384    }385}386387impl fmt::Display for ExternAbi {388    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {389        write!(f, "\"{}\"", self.as_str())390    }391}

Code quality findings 5

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
$e_name::ALL_VARIANTS[index]
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match s {
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use ExternAbi::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings