Skip to content

stabilize c-style varargs for system, sysv64, win64, efiapi, aapcs #144066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions compiler/rustc_abi/src/extern_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,22 +235,33 @@ impl ExternAbi {
matches!(self, Rust | RustCall | RustCold)
}

pub fn supports_varargs(self) -> bool {
/// Returns whether the ABI supports C variadics.
///
/// Note that this is insta-stable if the ABI is stable! If you add a new unstable ABI that
/// supports variadics, it is fine to just add it here. But if you want to equip an existing
/// *stable* ABI with support for variadics, you cannot just add it here. Instead, add a feature
/// gate check in `require_supported_abi_if_c_variadic`, and only move it here once variadic
/// support os stable.
pub fn supports_c_variadic(self) -> bool {
// * 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 => true,
| Self::EfiApi
| Self::System { .. } => true,
_ => false,
}
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ declare_features! (
(accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)),
/// Allows arbitrary expressions in key-value attributes at parse time.
(accepted, extended_key_value_attributes, "1.54.0", Some(78835)),
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
/// for functions with varargs.
(accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)),
/// Allows resolving absolute paths as paths from other crates.
(accepted, extern_absolute_paths, "1.30.0", Some(44660)),
/// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
Expand All @@ -212,6 +215,8 @@ declare_features! (
(accepted, extern_crate_self, "1.34.0", Some(56409)),
/// Allows access to crate names passed via `--extern` through prelude.
(accepted, extern_prelude, "1.30.0", Some(44660)),
/// Allows using `system` as a calling convention with varargs.
(accepted, extern_system_varargs, "CURRENT_RUSTC_VERSION", Some(136946)),
/// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`.
(accepted, f16c_target_feature, "1.68.0", Some(44839)),
/// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,11 +491,6 @@ declare_features! (
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `#[export_stable]` which indicates that an item is exportable.
(incomplete, export_stable, "1.88.0", Some(139939)),
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
/// for functions with varargs.
(unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
/// Allows using `system` as a calling convention with varargs.
(unstable, extern_system_varargs, "1.86.0", Some(136946)),
/// Allows defining `extern type`s.
(unstable, extern_types, "1.23.0", Some(43467)),
/// Allow using 128-bit (quad precision) floating point numbers.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().fn_sig(def_id);
let item = tcx.hir_foreign_item(item);
let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() };
require_c_abi_if_c_variadic(tcx, sig.decl, abi, item.span);
check_c_variadic_abi(tcx, sig.decl, abi, item.span);
}
DefKind::Static { .. } => {
tcx.ensure_ok().codegen_fn_attrs(def_id);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ use tracing::debug;

use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys;
use self::region::region_scope_tree;
use crate::{errors, require_c_abi_if_c_variadic};
use crate::{check_c_variadic_abi, errors};

/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
pub(super) fn provide(providers: &mut Providers) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ use rustc_trait_selection::traits::{self, FulfillmentError};
use tracing::{debug, instrument};

use crate::check::check_abi;
use crate::check_c_variadic_abi;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;

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

Ty::new_fn_ptr(
tcx,
Expand Down
47 changes: 11 additions & 36 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ use rustc_middle::middle;
use rustc_middle::mir::interpret::GlobalId;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;

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

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

fn require_c_abi_if_c_variadic(
tcx: TyCtxt<'_>,
decl: &hir::FnDecl<'_>,
abi: ExternAbi,
span: Span,
) {
// ABIs which can stably use varargs
if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) {
fn check_c_variadic_abi(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: ExternAbi, span: Span) {
if !decl.c_variadic {
// Not even a variadic function.
return;
}

// ABIs with feature-gated stability
let extended_abi_support = tcx.features().extended_varargs_abi_support();
let extern_system_varargs = tcx.features().extern_system_varargs();

// If the feature gate has been enabled, we can stop here
if extern_system_varargs && let ExternAbi::System { .. } = abi {
if abi.supports_c_variadic() {
// Variadics are stable.
return;
};
if extended_abi_support && abi.supports_varargs() {
return;
};
}

// Looks like we need to pick an error to emit.
// Is there any feature which we could have enabled to make this work?
let unstable_explain =
format!("C-variadic functions with the {abi} calling convention are unstable");
match abi {
ExternAbi::System { .. } => {
feature_err(&tcx.sess, sym::extern_system_varargs, span, unstable_explain)
}
abi if abi.supports_varargs() => {
feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, unstable_explain)
}
_ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention {
// Everything else is unstable.
tcx.dcx()
.create_err(errors::VariadicFunctionCompatibleConvention {
span,
convention: &format!("{abi}"),
}),
}
.emit();
})
.emit();
}

/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
Expand Down
1 change: 0 additions & 1 deletion library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@
#![feature(doc_masked)]
#![feature(doc_notable_trait)]
#![feature(dropck_eyepatch)]
#![feature(extended_varargs_abi_support)]
#![feature(f16)]
#![feature(f128)]
#![feature(ffi_const)]
Expand Down

This file was deleted.

85 changes: 85 additions & 0 deletions tests/codegen/cffi/c-variadic-ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//! Test calling variadic functions with various ABIs.
//@ add-core-stubs
//@ compile-flags: -Z merge-functions=disabled
//@ revisions: x86_32 x86_32_win x86_64 aarch64 arm32
//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86_64] needs-llvm-components: x86
//@[x86_32_win] compile-flags: --target i686-pc-windows-msvc
//@[x86_32_win] needs-llvm-components: x86
//@[x86_32] compile-flags: --target i686-unknown-linux-gnu
//@[x86_32] needs-llvm-components: x86
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
//@[aarch64] needs-llvm-components: aarch64
//@[arm32] compile-flags: --target armv7-unknown-linux-gnueabihf
//@[arm32] needs-llvm-components: arm
#![crate_type = "lib"]
#![feature(no_core)]
#![no_core]

extern crate minicore;

// CHECK-LABEL: @c
#[unsafe(no_mangle)]
fn c(f: extern "C" fn(i32, ...)) {
// CHECK: call void (i32, ...)
f(22, 44);
}

// CHECK-LABEL: @system
#[unsafe(no_mangle)]
fn system(f: extern "system" fn(i32, ...)) {
// Crucially, this is *always* the C calling convention, even on Windows.
// CHECK: call void (i32, ...)
f(22, 44);
}

// x86_32-LABEL: @cdecl
#[unsafe(no_mangle)]
#[cfg(target_arch = "x86")]
fn cdecl(f: extern "cdecl" fn(i32, ...)) {
// x86_32: call void (i32, ...)
f(22, 44);
}

// x86_64-LABEL: @sysv
#[unsafe(no_mangle)]
#[cfg(target_arch = "x86_64")]
fn sysv(f: extern "sysv64" fn(i32, ...)) {
// x86_64: call x86_64_sysvcc void (i32, ...)
f(22, 44);
}

// x86_64-LABEL: @win
#[unsafe(no_mangle)]
#[cfg(target_arch = "x86_64")]
fn win(f: extern "win64" fn(i32, ...)) {
// x86_64: call win64cc void (i32, ...)
f(22, 44);
}

// CHECK-LABEL: @efiapi
#[unsafe(no_mangle)]
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "x86",
target_arch = "x86_64"
))]
fn efiapi(f: extern "efiapi" fn(i32, ...)) {
// x86_32: call void (i32, ...)
// x86_32_win: call void (i32, ...)
// x86_64: call win64cc void (i32, ...)
// aarch64: call void (i32, ...)
// arm32: call arm_aapcscc void (i32, ...)
f(22, 44);
}

// arm32-LABEL: @aapcs
#[unsafe(no_mangle)]
#[cfg(target_arch = "arm")]
fn aapcs(f: extern "aapcs" fn(i32, ...)) {
// arm32: call arm_aapcscc void (i32, ...)
f(22, 44);
}
2 changes: 0 additions & 2 deletions tests/ui/abi/unsupported-varargs-fnptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
// We have to use this flag to force ABI computation of an invalid ABI
//@ compile-flags: -Clink-dead-code

#![feature(extended_varargs_abi_support)]

// sometimes fn ptrs with varargs make layout and ABI computation ICE
// as found in https://github.com/rust-lang/rust/issues/142107

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/abi/unsupported-varargs-fnptr.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0570]: "aapcs" is not a supported ABI for the current target
--> $DIR/unsupported-varargs-fnptr.rs:14:20
--> $DIR/unsupported-varargs-fnptr.rs:12:20
|
LL | fn aapcs(f: extern "aapcs" fn(usize, ...)) {
| ^^^^^^^
Expand Down
16 changes: 0 additions & 16 deletions tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs

This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions tests/ui/c-variadic/variadic-ffi-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ extern "stdcall" {
//~^ ERROR: C-variadic functions with the "stdcall" calling convention are not supported
}

fn baz(f: extern "Rust" fn(usize, ...)) {
//~^ ERROR: C-variadic functions with the "Rust" calling convention are not supported
f(22, 44);
}

extern "C" {
fn foo(f: isize, x: u8, ...);
}
Expand Down
Loading
Loading