Skip to content

Commit 496a9d6

Browse files
committed
stabilize varargs for system, sysv64, win64, efiapi, aapcs
1 parent e27f16a commit 496a9d6

23 files changed

+166
-190
lines changed

compiler/rustc_abi/src/extern_abi.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,22 +235,33 @@ impl ExternAbi {
235235
matches!(self, Rust | RustCall | RustCold)
236236
}
237237

238-
pub fn supports_varargs(self) -> bool {
238+
/// Returns whether the ABI supports C variadics.
239+
///
240+
/// Note that this is insta-stable if the ABI is stable! If you add a new unstable ABI that
241+
/// supports variadics, it is fine to just add it here. But if you want to equip an existing
242+
/// *stable* ABI with support for variadics, you cannot just add it here. Instead, add a feature
243+
/// gate check in `require_supported_abi_if_c_variadic`, and only move it here once variadic
244+
/// support os stable.
245+
pub fn supports_c_variadic(self) -> bool {
239246
// * C and Cdecl obviously support varargs.
240247
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
241248
// * EfiApi is based on Win64 or C, so it also supports it.
249+
// * System automatically falls back to C when used with variadics, therefore supports it.
242250
//
243251
// * Stdcall does not, because it would be impossible for the callee to clean
244252
// up the arguments. (callee doesn't know how many arguments are there)
245253
// * Same for Fastcall, Vectorcall and Thiscall.
246254
// * Other calling conventions are related to hardware or the compiler itself.
255+
//
256+
// All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
247257
match self {
248258
Self::C { .. }
249259
| Self::Cdecl { .. }
250260
| Self::Aapcs { .. }
251261
| Self::Win64 { .. }
252262
| Self::SysV64 { .. }
253-
| Self::EfiApi => true,
263+
| Self::EfiApi
264+
| Self::System { .. } => true,
254265
_ => false,
255266
}
256267
}

compiler/rustc_feature/src/accepted.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ declare_features! (
203203
(accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
204204
/// Allows arbitrary expressions in key-value attributes at parse time.
205205
(accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
206+
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
207+
/// for functions with varargs.
208+
(accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)),
206209
/// Allows resolving absolute paths as paths from other crates.
207210
(accepted, extern_absolute_paths, "1.30.0", Some(44660)),
208211
/// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
@@ -212,6 +215,8 @@ declare_features! (
212215
(accepted, extern_crate_self, "1.34.0", Some(56409)),
213216
/// Allows access to crate names passed via `--extern` through prelude.
214217
(accepted, extern_prelude, "1.30.0", Some(44660)),
218+
/// Allows using `system` as a calling convention with varargs.
219+
(accepted, extern_system_varargs, "CURRENT_RUSTC_VERSION", Some(136946)),
215220
/// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`.
216221
(accepted, f16c_target_feature, "1.68.0", Some(44839)),
217222
/// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.

compiler/rustc_feature/src/unstable.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -491,11 +491,6 @@ declare_features! (
491491
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
492492
/// Allows using `#[export_stable]` which indicates that an item is exportable.
493493
(incomplete, export_stable, "1.88.0", Some(139939)),
494-
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
495-
/// for functions with varargs.
496-
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
497-
/// Allows using `system` as a calling convention with varargs.
498-
(unstable, extern_system_varargs, "1.86.0", Some(136946)),
499494
/// Allows defining `extern type`s.
500495
(unstable, extern_types, "1.23.0", Some(43467)),
501496
/// Allow using 128-bit (quad precision) floating point numbers.

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
977977
tcx.ensure_ok().fn_sig(def_id);
978978
let item = tcx.hir_foreign_item(item);
979979
let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() };
980-
require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
980+
check_c_variadic_abi(tcx, sig.decl, abi, item.span);
981981
}
982982
DefKind::Static { .. } => {
983983
tcx.ensure_ok().codegen_fn_attrs(def_id);

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ use tracing::debug;
9898

9999
use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
100100
use self::region::region_scope_tree;
101-
use crate::{errors, require_c_abi_if_c_variadic};
101+
use crate::{check_c_variadic_abi, errors};
102102

103103
/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
104104
pub(super) fn provide(providers: &mut Providers) {

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError};
5252
use tracing::{debug, instrument};
5353

5454
use crate::check::check_abi;
55+
use crate::check_c_variadic_abi;
5556
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
5657
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
5758
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
5859
use crate::middle::resolve_bound_vars as rbv;
59-
use crate::require_c_abi_if_c_variadic;
6060

6161
/// A path segment that is semantically allowed to have generic arguments.
6262
#[derive(Debug)]
@@ -2403,7 +2403,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
24032403
Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t)))
24042404
}
24052405
hir::TyKind::FnPtr(bf) => {
2406-
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span);
2406+
check_c_variadic_abi(tcx, bf.decl, bf.abi, hir_ty.span);
24072407

24082408
Ty::new_fn_ptr(
24092409
tcx,

compiler/rustc_hir_analysis/src/lib.rs

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,6 @@ use rustc_middle::middle;
9898
use rustc_middle::mir::interpret::GlobalId;
9999
use rustc_middle::query::Providers;
100100
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
101-
use rustc_session::parse::feature_err;
102-
use rustc_span::symbol::sym;
103101
use rustc_span::{ErrorGuaranteed, Span};
104102
use rustc_trait_selection::traits;
105103

@@ -108,46 +106,23 @@ use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer};
108106

109107
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
110108

111-
fn require_c_abi_if_c_variadic(
112-
tcx: TyCtxt<'_>,
113-
decl: &hir::FnDecl<'_>,
114-
abi: ExternAbi,
115-
span: Span,
116-
) {
117-
// ABIs which can stably use varargs
118-
if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) {
109+
fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, span: Span) {
110+
if !decl.c_variadic {
111+
// Not even a variadic function.
119112
return;
120113
}
121-
122-
// ABIs with feature-gated stability
123-
let extended_abi_support = tcx.features().extended_varargs_abi_support();
124-
let extern_system_varargs = tcx.features().extern_system_varargs();
125-
126-
// If the feature gate has been enabled, we can stop here
127-
if extern_system_varargs && let ExternAbi::System { .. } = abi {
114+
if abi.supports_c_variadic() {
115+
// Variadics are stable.
128116
return;
129-
};
130-
if extended_abi_support && abi.supports_varargs() {
131-
return;
132-
};
117+
}
133118

134-
// Looks like we need to pick an error to emit.
135-
// Is there any feature which we could have enabled to make this work?
136-
let unstable_explain =
137-
format!("C-variadic functions with the {abi} calling convention are unstable");
138-
match abi {
139-
ExternAbi::System { .. } => {
140-
feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain)
141-
}
142-
abi if abi.supports_varargs() => {
143-
feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain)
144-
}
145-
_ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention {
119+
// Everything else is unstable.
120+
tcx.dcx()
121+
.create_err(errors::VariadicFunctionCompatibleConvention {
146122
span,
147123
convention: &format!("{abi}"),
148-
}),
149-
}
150-
.emit();
124+
})
125+
.emit();
151126
}
152127

153128
/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]

library/std/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@
289289
#![feature(doc_masked)]
290290
#![feature(doc_notable_trait)]
291291
#![feature(dropck_eyepatch)]
292-
#![feature(extended_varargs_abi_support)]
293292
#![feature(f16)]
294293
#![feature(f128)]
295294
#![feature(ffi_const)]

src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/codegen/cffi/c-variadic-ffi.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//! Test calling variadic functions with various ABIs.
2+
//@ add-core-stubs
3+
//@ compile-flags: -Z merge-functions=disabled
4+
//@ revisions: x86_32 x86_32_win x86_64 aarch64 arm32
5+
//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
6+
//@[x86_64] needs-llvm-components: x86
7+
//@[x86_32_win] compile-flags: --target i686-pc-windows-msvc
8+
//@[x86_32_win] needs-llvm-components: x86
9+
//@[x86_32] compile-flags: --target i686-unknown-linux-gnu
10+
//@[x86_32] needs-llvm-components: x86
11+
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
12+
//@[aarch64] needs-llvm-components: aarch64
13+
//@[arm32] compile-flags: --target armv7-unknown-linux-gnueabihf
14+
//@[arm32] needs-llvm-components: arm
15+
#![crate_type = "lib"]
16+
#![feature(no_core)]
17+
#![no_core]
18+
19+
extern crate minicore;
20+
21+
// CHECK-LABEL: @c
22+
#[unsafe(no_mangle)]
23+
fn c(f: extern "C" fn(i32, ...)) {
24+
// CHECK: call void (i32, ...)
25+
f(22, 44);
26+
}
27+
28+
// CHECK-LABEL: @system
29+
#[unsafe(no_mangle)]
30+
fn system(f: extern "system" fn(i32, ...)) {
31+
// Crucially, this is *always* the C calling convention, even on Windows.
32+
// CHECK: call void (i32, ...)
33+
f(22, 44);
34+
}
35+
36+
// x86_32-LABEL: @cdecl
37+
#[unsafe(no_mangle)]
38+
#[cfg(target_arch = "x86")]
39+
fn cdecl(f: extern "cdecl" fn(i32, ...)) {
40+
// x86_32: call void (i32, ...)
41+
f(22, 44);
42+
}
43+
44+
// x86_64-LABEL: @sysv
45+
#[unsafe(no_mangle)]
46+
#[cfg(target_arch = "x86_64")]
47+
fn sysv(f: extern "sysv64" fn(i32, ...)) {
48+
// x86_64: call x86_64_sysvcc void (i32, ...)
49+
f(22, 44);
50+
}
51+
52+
// x86_64-LABEL: @win
53+
#[unsafe(no_mangle)]
54+
#[cfg(target_arch = "x86_64")]
55+
fn win(f: extern "win64" fn(i32, ...)) {
56+
// x86_64: call win64cc void (i32, ...)
57+
f(22, 44);
58+
}
59+
60+
// CHECK-LABEL: @efiapi
61+
#[unsafe(no_mangle)]
62+
#[cfg(any(
63+
target_arch = "arm",
64+
target_arch = "aarch64",
65+
target_arch = "riscv32",
66+
target_arch = "riscv64",
67+
target_arch = "x86",
68+
target_arch = "x86_64"
69+
))]
70+
fn efiapi(f: extern "efiapi" fn(i32, ...)) {
71+
// x86_32: call void (i32, ...)
72+
// x86_32_win: call void (i32, ...)
73+
// x86_64: call win64cc void (i32, ...)
74+
// aarch64: call void (i32, ...)
75+
// arm32: call arm_aapcscc void (i32, ...)
76+
f(22, 44);
77+
}
78+
79+
// arm32-LABEL: @aapcs
80+
#[unsafe(no_mangle)]
81+
#[cfg(target_arch = "arm")]
82+
fn aapcs(f: extern "aapcs" fn(i32, ...)) {
83+
// arm32: call arm_aapcscc void (i32, ...)
84+
f(22, 44);
85+
}

0 commit comments

Comments
 (0)