Add Swift function call ABI

Adds an unstable `extern "Swift"` ABI behind the `abi_swift` feature
gate, mapping to LLVM's `swiftcc` calling convention. Cranelift and
GCC backends fall back to the platform default since they have no
equivalent.
This commit is contained in:
Dirkjan Ochtman
2026-04-26 12:09:05 +02:00
parent 2aabf3ce05
commit f49e45101c
23 changed files with 233 additions and 3 deletions
+6
View File
@@ -32,6 +32,10 @@ pub enum CanonAbi {
/// An ABI that rustc does not know how to call or define.
Custom,
/// Swift calling convention, exposed via LLVM's `swiftcc`. Cross-platform
/// and not tied to a specific target architecture.
Swift,
/// ABIs relevant to 32-bit Arm targets
Arm(ArmCall),
/// ABI relevant to GPUs: the entry point for a GPU kernel
@@ -58,6 +62,7 @@ pub fn is_rustic_abi(self) -> bool {
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Swift
| CanonAbi::Arm(_)
| CanonAbi::GpuKernel
| CanonAbi::Interrupt(_)
@@ -77,6 +82,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Swift => ExternAbi::Swift,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
ArmCall::CCmseNonSecureCall => ExternAbi::CmseNonSecureCall,
+7 -1
View File
@@ -62,6 +62,10 @@ pub enum ExternAbi {
/// and only valid on platforms that have a UEFI standard
EfiApi,
/// Swift's calling convention, used to interoperate with Swift code without
/// going through C as an intermediary.
Swift,
/* arm */
/// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this
Aapcs {
@@ -173,6 +177,7 @@ fn from_str(s: &str) -> Result<$e_name, Self::Err> {
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",
@@ -348,7 +353,8 @@ pub fn supports_guaranteed_tail_call(self) -> bool {
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. }
| Self::RustPreserveNone => true,
| Self::RustPreserveNone
| Self::Swift => true,
}
}
}
@@ -144,5 +144,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
ExternAbi::Custom => {
Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental })
}
ExternAbi::Swift => {
Err(UnstableAbi { abi, feature: sym::abi_swift, explain: GateReason::Experimental })
}
}
}
@@ -424,6 +424,7 @@ fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident,
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::Swift
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => { /* nothing to check */ }
@@ -70,7 +70,7 @@ pub(crate) fn conv_to_call_conv(
_ => default_call_conv,
},
CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => {
CanonAbi::Interrupt(_) | CanonAbi::Arm(_) | CanonAbi::Swift => {
sess.dcx().fatal("call conv {c:?} is not yet implemented")
}
CanonAbi::GpuKernel => {
+2
View File
@@ -245,6 +245,8 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option<FnAttri
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => return None,
// gcc/gccjit does not have anything for Swift's calling convention.
CanonAbi::Swift => panic!("gcc/gccjit backend does not support Swift calling convention"),
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
+1
View File
@@ -722,6 +722,7 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm:
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => llvm::CCallConv,
CanonAbi::Swift => llvm::SwiftCallConv,
CanonAbi::GpuKernel => match &sess.target.arch {
Arch::AmdGpu => llvm::AmdgpuKernel,
Arch::Nvptx64 => llvm::PtxKernel,
@@ -166,6 +166,7 @@ pub(crate) enum CallConv {
ColdCallConv = 9,
PreserveMost = 14,
PreserveAll = 15,
SwiftCallConv = 16,
Tail = 18,
PreserveNone = 21,
X86StdcallCallConv = 64,
+2
View File
@@ -380,6 +380,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
(unstable, abi_ptx, "1.15.0", Some(38788)),
/// Allows `extern "riscv-interrupt-m" fn()` and `extern "riscv-interrupt-s" fn()`.
(unstable, abi_riscv_interrupt, "1.73.0", Some(111889)),
/// Allows `extern "Swift" fn()`.
(unstable, abi_swift, "CURRENT_RUSTC_VERSION", Some(156481)),
/// Allows `extern "x86-interrupt" fn()`.
(unstable, abi_x86_interrupt, "1.17.0", Some(40180)),
/// Allows additional const parameter types, such as `[u8; 10]` or user defined types
+1
View File
@@ -202,6 +202,7 @@ pub(crate) fn check_call_abi(&self, abi: ExternAbi, span: Span) {
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::Swift
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => {}
}
+1
View File
@@ -1291,6 +1291,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
| RiscvInterruptM
| RiscvInterruptS
| RustInvalid
| Swift
| Unadjusted => false,
Rust | RustCall | RustCold | RustPreserveNone => tcx.sess.panic_strategy().unwinds(),
}
+2
View File
@@ -458,6 +458,8 @@ pub enum CallConvention {
Custom,
Swift,
// Target-specific calling conventions.
ArmAapcs,
CCmseNonSecureCall,
+1
View File
@@ -1169,6 +1169,7 @@ pub enum Abi {
RustPreserveNone,
RustInvalid,
Custom,
Swift,
}
/// A binder represents a possibly generic type and its bound vars.
@@ -619,6 +619,7 @@ fn internal<'tcx>(
Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
Abi::RustPreserveNone => rustc_abi::ExternAbi::RustPreserveNone,
Abi::Custom => rustc_abi::ExternAbi::Custom,
Abi::Swift => rustc_abi::ExternAbi::Swift,
}
}
}
@@ -126,6 +126,7 @@ fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>)
CanonAbi::RustCold => CallConvention::Cold,
CanonAbi::RustPreserveNone => CallConvention::PreserveNone,
CanonAbi::Custom => CallConvention::Custom,
CanonAbi::Swift => CallConvention::Swift,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => CallConvention::ArmAapcs,
ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
@@ -1050,6 +1050,7 @@ fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>)
ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
ExternAbi::Custom => Abi::Custom,
ExternAbi::Swift => Abi::Swift,
}
}
}
+1
View File
@@ -355,6 +355,7 @@
abi_msp430_interrupt,
abi_ptx,
abi_riscv_interrupt,
abi_swift,
abi_sysv64,
abi_thiscall,
abi_unadjusted,
+15 -1
View File
@@ -65,7 +65,9 @@ pub fn from_target(target: &Target) -> Self {
_ => ArchKind::Other,
};
let os = if target.is_like_windows {
let os = if target.is_like_darwin {
OsKind::Apple
} else if target.is_like_windows {
OsKind::Windows
} else if target.is_like_vexos {
OsKind::VEXos
@@ -80,6 +82,15 @@ pub fn from_target(target: &Target) -> Self {
pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMapping {
let AbiMap { os, arch } = *self;
if extern_abi == ExternAbi::Swift {
// Per https://www.swift.org/blog/abi-stability-and-more/, Swift's ABI
// is only stable on Apple platforms, so we reject it elsewhere.
match os {
OsKind::Apple => {}
_ => return AbiMapping::Invalid,
}
}
let canon_abi = match (extern_abi, arch) {
// infallible lowerings
(ExternAbi::C { .. }, _) => CanonAbi::C,
@@ -92,6 +103,8 @@ pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMap
(ExternAbi::Custom, _) => CanonAbi::Custom,
(ExternAbi::Swift, _) => CanonAbi::Swift,
(ExternAbi::System { .. }, ArchKind::X86)
if os == OsKind::Windows && !has_c_varargs =>
{
@@ -213,6 +226,7 @@ enum ArchKind {
#[derive(Debug, PartialEq, Copy, Clone)]
enum OsKind {
Apple,
Windows,
VEXos,
Other,
+33
View File
@@ -0,0 +1,33 @@
//@ add-minicore
//@ needs-llvm-components: x86
//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
//@ ignore-backends: gcc
#![no_core]
#![feature(no_core, lang_items, abi_swift)]
extern crate minicore;
use minicore::*;
// The Swift ABI is only stable on Apple platforms, so it must be rejected
// on other targets even when the `abi_swift` feature gate is enabled.
extern "Swift" fn f() {} //~ ERROR is not a supported ABI
trait T {
extern "Swift" fn m(); //~ ERROR is not a supported ABI
extern "Swift" fn dm() {} //~ ERROR is not a supported ABI
}
struct S;
impl T for S {
extern "Swift" fn m() {} //~ ERROR is not a supported ABI
}
impl S {
extern "Swift" fn im() {} //~ ERROR is not a supported ABI
}
type TA = extern "Swift" fn(); //~ ERROR is not a supported ABI
extern "Swift" {} //~ ERROR is not a supported ABI
+45
View File
@@ -0,0 +1,45 @@
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:14:8
|
LL | extern "Swift" fn f() {}
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:17:12
|
LL | extern "Swift" fn m();
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:19:12
|
LL | extern "Swift" fn dm() {}
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:24:12
|
LL | extern "Swift" fn m() {}
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:28:12
|
LL | extern "Swift" fn im() {}
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:31:18
|
LL | type TA = extern "Swift" fn();
| ^^^^^^^
error[E0570]: "Swift" is not a supported ABI for the current target
--> $DIR/swift-abi-non-apple.rs:33:8
|
LL | extern "Swift" {}
| ^^^^^^^
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0570`.
@@ -0,0 +1,33 @@
//@ add-minicore
//@ needs-llvm-components: aarch64
//@ compile-flags: --target=aarch64-apple-darwin --crate-type=rlib
//@ ignore-backends: gcc
#![no_core]
#![feature(no_core, lang_items)]
extern crate minicore;
use minicore::*;
// Test that the "Swift" ABI is feature-gated, and cannot be used when
// the `abi_swift` feature gate is not used.
extern "Swift" fn f() {} //~ ERROR "Swift" ABI is experimental
trait T {
extern "Swift" fn m(); //~ ERROR "Swift" ABI is experimental
extern "Swift" fn dm() {} //~ ERROR "Swift" ABI is experimental
}
struct S;
impl T for S {
extern "Swift" fn m() {} //~ ERROR "Swift" ABI is experimental
}
impl S {
extern "Swift" fn im() {} //~ ERROR "Swift" ABI is experimental
}
type TA = extern "Swift" fn(); //~ ERROR "Swift" ABI is experimental
extern "Swift" {} //~ ERROR "Swift" ABI is experimental
@@ -0,0 +1,73 @@
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:14:8
|
LL | extern "Swift" fn f() {}
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:17:12
|
LL | extern "Swift" fn m();
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:19:12
|
LL | extern "Swift" fn dm() {}
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:24:12
|
LL | extern "Swift" fn m() {}
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:28:12
|
LL | extern "Swift" fn im() {}
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:31:18
|
LL | type TA = extern "Swift" fn();
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the extern "Swift" ABI is experimental and subject to change
--> $DIR/feature-gate-abi_swift.rs:33:8
|
LL | extern "Swift" {}
| ^^^^^^^
|
= note: see issue #156481 <https://github.com/rust-lang/rust/issues/156481> for more information
= help: add `#![feature(abi_swift)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0658`.
@@ -1,6 +1,7 @@
C
C-unwind
Rust
Swift
aapcs
aapcs-unwind
avr-interrupt