compiler/rustc_abi/src/extern_abi.rs RUST 374 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_hasher::{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    /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.53    /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!54    Unadjusted,5556    /// An ABI that rustc does not know how to call or define. Functions with this ABI can57    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only58    /// be called from inline assembly.59    Custom,6061    /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias62    /// and only valid on platforms that have a UEFI standard63    EfiApi,6465    /* arm */66    /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this67    Aapcs {68        unwind: bool,69    },70    /// extremely constrained barely-C ABI for TrustZone71    CmseNonSecureCall,72    /// extremely constrained barely-C ABI for TrustZone73    CmseNonSecureEntry,7475    /* gpu */76    /// An entry-point function called by the GPU's host77    GpuKernel,78    /// An entry-point function called by the GPU's host79    // FIXME: why do we have two of these?80    PtxKernel,8182    /* interrupt */83    AvrInterrupt,84    AvrNonBlockingInterrupt,85    Msp430Interrupt,86    RiscvInterruptM,87    RiscvInterruptS,88    X86Interrupt,8990    /* x86 */91    /// `ExternAbi::C` but spelled funny because x8692    Cdecl {93        unwind: bool,94    },95    /// gnu-stdcall on "unix" and win-stdcall on "windows"96    Stdcall {97        unwind: bool,98    },99    /// gnu-fastcall on "unix" and win-fastcall on "windows"100    Fastcall {101        unwind: bool,102    },103    /// windows C++ ABI104    Thiscall {105        unwind: bool,106    },107    /// uses AVX and stuff108    Vectorcall {109        unwind: bool,110    },111112    /* x86_64 */113    SysV64 {114        unwind: bool,115    },116    Win64 {117        unwind: bool,118    },119}120121macro_rules! abi_impls {122    ($e_name:ident = {123        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*124    }) => {125        impl $e_name {126            pub const ALL_VARIANTS: &[Self] = &[127                $($e_name::$variant $({ unwind: $uw })*,)*128            ];129            pub const fn as_str(&self) -> &'static str {130                match self {131                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*132                }133            }134            // FIXME(FnSigKind): when PartialEq is stably const, use it instead135            const fn internal_const_eq(&self, other: &Self) -> bool {136                match (self, other) {137                    $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*138                    _ => false,139                }140            }141            // ALL_VARIANTS.iter().position(|v| v == self), but const142            pub const fn as_packed(&self) -> u8 {143                let mut index = 0;144                while index < $e_name::ALL_VARIANTS.len() {145                    if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {146                        return index as u8;147                    }148                    index += 1;149                }150                panic!("unreachable: invalid ExternAbi variant");151            }152            pub const fn from_packed(index: u8) -> Self {153                let index = index as usize;154                assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");155                $e_name::ALL_VARIANTS[index]156            }157        }158159        impl ::core::str::FromStr for $e_name {160            type Err = AbiFromStrErr;161            fn from_str(s: &str) -> Result<$e_name, Self::Err> {162                match s {163                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*164                    _ => Err(AbiFromStrErr::Unknown),165                }166            }167        }168    }169}170171abi_impls! {172    ExternAbi = {173            C { unwind: false } =><= "C",174            C { unwind: true } =><= "C-unwind",175            Rust =><= "Rust",176            Aapcs { unwind: false } =><= "aapcs",177            Aapcs { unwind: true } =><= "aapcs-unwind",178            AvrInterrupt =><= "avr-interrupt",179            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",180            Cdecl { unwind: false } =><= "cdecl",181            Cdecl { unwind: true } =><= "cdecl-unwind",182            CmseNonSecureCall =><= "cmse-nonsecure-call",183            CmseNonSecureEntry =><= "cmse-nonsecure-entry",184            Custom =><= "custom",185            EfiApi =><= "efiapi",186            Fastcall { unwind: false } =><= "fastcall",187            Fastcall { unwind: true } =><= "fastcall-unwind",188            GpuKernel =><= "gpu-kernel",189            Msp430Interrupt =><= "msp430-interrupt",190            PtxKernel =><= "ptx-kernel",191            RiscvInterruptM =><= "riscv-interrupt-m",192            RiscvInterruptS =><= "riscv-interrupt-s",193            RustCall =><= "rust-call",194            RustCold =><= "rust-cold",195            RustInvalid =><= "rust-invalid",196            RustPreserveNone =><= "rust-preserve-none",197            Stdcall { unwind: false } =><= "stdcall",198            Stdcall { unwind: true } =><= "stdcall-unwind",199            System { unwind: false } =><= "system",200            System { unwind: true } =><= "system-unwind",201            SysV64 { unwind: false } =><= "sysv64",202            SysV64 { unwind: true } =><= "sysv64-unwind",203            Thiscall { unwind: false } =><= "thiscall",204            Thiscall { unwind: true } =><= "thiscall-unwind",205            Unadjusted =><= "unadjusted",206            Vectorcall { unwind: false } =><= "vectorcall",207            Vectorcall { unwind: true } =><= "vectorcall-unwind",208            Win64 { unwind: false } =><= "win64",209            Win64 { unwind: true } =><= "win64-unwind",210            X86Interrupt =><= "x86-interrupt",211    }212}213214impl Ord for ExternAbi {215    fn cmp(&self, rhs: &Self) -> Ordering {216        self.as_str().cmp(rhs.as_str())217    }218}219220impl PartialOrd for ExternAbi {221    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {222        Some(self.cmp(rhs))223    }224}225226impl PartialEq for ExternAbi {227    fn eq(&self, rhs: &Self) -> bool {228        self.cmp(rhs) == Ordering::Equal229    }230}231232impl Eq for ExternAbi {}233234impl Hash for ExternAbi {235    fn hash<H: Hasher>(&self, state: &mut H) {236        self.as_str().hash(state);237        // double-assurance of a prefix breaker238        u32::from_be_bytes(*b"ABI\0").hash(state);239    }240}241242#[cfg(feature = "nightly")]243impl StableHash for ExternAbi {244    #[inline]245    fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {246        Hash::hash(self, hasher);247    }248}249250#[cfg(feature = "nightly")]251impl StableOrd for ExternAbi {252    const CAN_USE_UNSTABLE_SORT: bool = true;253254    // because each ABI is hashed like a string, there is no possible instability255    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();256}257258#[cfg(feature = "nightly")]259rustc_error_messages::into_diag_arg_using_display!(ExternAbi);260261#[cfg(feature = "nightly")]262#[derive(Debug)]263pub enum CVariadicStatus {264    NotSupported,265    Stable,266    Unstable { feature: Symbol },267}268269impl ExternAbi {270    /// An ABI "like Rust"271    ///272    /// These ABIs are fully controlled by the Rust compiler, which means they273    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`274    /// - often diverge from the C ABI275    /// - are subject to change between compiler versions276    pub fn is_rustic_abi(self) -> bool {277        use ExternAbi::*;278        matches!(self, Rust | RustCall | RustCold | RustPreserveNone)279    }280281    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*282    /// of such functions via `extern` blocks; there's a separate check during AST construction283    /// guarding *definitions* of variadic functions.284    #[cfg(feature = "nightly")]285    pub fn supports_c_variadic(self) -> CVariadicStatus {286        // * C and Cdecl obviously support varargs.287        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.288        // * EfiApi is based on Win64 or C, so it also supports it.289        // * System automatically falls back to C when used with variadics, therefore supports it.290        //291        // * Stdcall does not, because it would be impossible for the callee to clean292        //   up the arguments. (callee doesn't know how many arguments are there)293        // * Same for Fastcall, Vectorcall and Thiscall.294        // * Other calling conventions are related to hardware or the compiler itself.295        //296        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.297        match self {298            Self::C { .. }299            | Self::Cdecl { .. }300            | Self::Aapcs { .. }301            | Self::Win64 { .. }302            | Self::SysV64 { .. }303            | Self::EfiApi304            | Self::System { .. } => CVariadicStatus::Stable,305            _ => CVariadicStatus::NotSupported,306        }307    }308309    /// Returns whether the ABI supports guaranteed tail calls.310    #[cfg(feature = "nightly")]311    pub fn supports_guaranteed_tail_call(self) -> bool {312        match self {313            Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {314                // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers315                // before returning, and hence cannot guarantee a tail call.316                false317            }318            Self::AvrInterrupt319            | Self::AvrNonBlockingInterrupt320            | Self::Msp430Interrupt321            | Self::RiscvInterruptM322            | Self::RiscvInterruptS323            | Self::X86Interrupt => {324                // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.325                false326            }327            Self::GpuKernel | Self::PtxKernel => {328                // See https://godbolt.org/z/jq5TE5jK1.329                false330            }331            Self::Custom => {332                // This ABI does not support calls at all (except via assembly).333                false334            }335            Self::C { .. }336            | Self::System { .. }337            | Self::Rust338            | Self::RustCall339            | Self::RustCold340            | Self::RustInvalid341            | Self::Unadjusted342            | Self::EfiApi343            | Self::Aapcs { .. }344            | Self::Cdecl { .. }345            | Self::Stdcall { .. }346            | Self::Fastcall { .. }347            | Self::Thiscall { .. }348            | Self::Vectorcall { .. }349            | Self::SysV64 { .. }350            | Self::Win64 { .. }351            | Self::RustPreserveNone => true,352        }353    }354}355356pub fn all_names() -> Vec<&'static str> {357    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()358}359360impl ExternAbi {361    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.362    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };363364    pub fn name(self) -> &'static str {365        self.as_str()366    }367}368369impl fmt::Display for ExternAbi {370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {371        write!(f, "\"{}\"", self.as_str())372    }373}

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.