(active, exclusive_range_pattern, "1.11.0", Some(37854), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(active, exhaustive_patterns, "1.13.0", Some(51085), None),
+ /// Allows using `efiapi`, `sysv64` and `win64` as calling convention
+ /// for functions with varargs.
+ (active, extended_varargs_abi_support, "1.65.0", Some(100189), None),
/// Allows defining `extern type`s.
(active, extern_types, "1.23.0", Some(43467), None),
/// Allows the use of `#[ffi_const]` on foreign functions.
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
-use rustc_session::config::EntryFnType;
+use rustc_session::{config::EntryFnType, parse::feature_err};
use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use bounds::Bounds;
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
- match (decl.c_variadic, abi) {
- // The function has the correct calling convention, or isn't a "C-variadic" function.
- (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
- // The function is a "C-variadic" function with an incorrect calling convention.
- (true, _) => {
- let mut err = struct_span_err!(
- tcx.sess,
+ const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention";
+ const CONVENTIONS_UNSTABLE: &str = "C, cdecl, win64, sysv64 or efiapi";
+ const CONVENTIONS_STABLE: &str = "C or cdecl";
+ const UNSTABLE_EXPLAIN: &str =
+ "using different calling convention than C or cdecl for varargs functions is unstable";
+
+ if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) {
+ return;
+ }
+
+ let extended_abi_support = tcx.features().extended_varargs_abi_support;
+ let conventions = match (extended_abi_support, abi.supports_varargs()) {
+ // User enabled additional ABI support for varargs and function ABI matches those ones.
+ (true, true) => return,
+
+ // Using this ABI would be ok, if the feature for additional ABI support was enabled.
+ // Return CONVENTIONS_STABLE, because we want the other error to look the same.
+ (false, true) => {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::extended_varargs_abi_support,
span,
- E0045,
- "C-variadic function must have C or cdecl calling convention"
- );
- err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
+ UNSTABLE_EXPLAIN,
+ )
+ .emit();
+ CONVENTIONS_STABLE
}
- }
+
+ (false, false) => CONVENTIONS_STABLE,
+ (true, false) => CONVENTIONS_UNSTABLE,
+ };
+
+ let mut err = struct_span_err!(tcx.sess, span, E0045, "{}, like {}", ERROR_HEAD, conventions);
+ err.span_label(span, ERROR_HEAD).emit();
}
fn require_same_types<'tcx>(
export_name,
expr,
extended_key_value_attributes,
+ extended_varargs_abi_support,
extern_absolute_paths,
extern_crate_item_prelude,
extern_crate_self,
RustCold,
}
+impl Abi {
+ pub fn supports_varargs(self) -> bool {
+ // * C and Cdecl obviously support varargs.
+ // * C can be based on SysV64 or Win64, so they must support varargs.
+ // * EfiApi is based on Win64 or C, so it also 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.
+ // * System can become Stdcall, so is also a no-no.
+ // * Other calling conventions are related to hardware or the compiler itself.
+ match self {
+ Self::C { .. }
+ | Self::Cdecl { .. }
+ | Self::Win64 { .. }
+ | Self::SysV64 { .. }
+ | Self::EfiApi => true,
+ _ => false,
+ }
+ }
+}
+
#[derive(Copy, Clone)]
pub struct AbiData {
abi: Abi,
--- /dev/null
+# `extended_varargs_abi_support`
+
+The tracking issue for this feature is: [#100189]
+
+[#100189]: https://github.com/rust-lang/rust/issues/100189
+
+------------------------
+
+This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling
+conventions on functions with varargs.
--- /dev/null
+#![feature(abi_efiapi)]
+
+fn efiapi(f: extern "efiapi" fn(usize, ...)) {
+ //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl
+ //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable
+ f(22, 44);
+}
+fn sysv(f: extern "sysv64" fn(usize, ...)) {
+ //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl
+ //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable
+ f(22, 44);
+}
+fn win(f: extern "win64" fn(usize, ...)) {
+ //~^ ERROR: C-variadic function must have a compatible calling convention, like C or cdecl
+ //~^^ ERROR: using different calling convention than C or cdecl for varargs functions is unstable
+ f(22, 44);
+}
+
+fn main() {}
--- /dev/null
+error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
+ |
+LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
+ = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
+
+error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
+ |
+LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
+ |
+LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
+ = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
+
+error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
+ |
+LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0658]: using different calling convention than C or cdecl for varargs functions is unstable
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
+ |
+LL | fn win(f: extern "win64" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
+ = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
+
+error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
+ |
+LL | fn win(f: extern "win64" fn(usize, ...)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0045, E0658.
+For more information about an error, try `rustc --explain E0045`.
trait Sized { }
extern "stdcall" {
- fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
+ fn printf(_: *const u8, ...);
+ //~^ ERROR: C-variadic function must have a compatible calling convention,
+ // like C, cdecl, win64, sysv64 or efiapi
}
extern "C" {
-error[E0045]: C-variadic function must have C or cdecl calling convention
+error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl
--> $DIR/variadic-ffi-1.rs:9:5
|
LL | fn printf(_: *const u8, ...);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied
- --> $DIR/variadic-ffi-1.rs:20:9
+ --> $DIR/variadic-ffi-1.rs:22:9
|
LL | foo();
| ^^^-- two arguments of type `isize` and `u8` are missing
|
note: function defined here
- --> $DIR/variadic-ffi-1.rs:13:8
+ --> $DIR/variadic-ffi-1.rs:15:8
|
LL | fn foo(f: isize, x: u8, ...);
| ^^^
| ~~~~~~~~~~~~~~~~~~~~~~~
error[E0060]: this function takes at least 2 arguments but 1 argument was supplied
- --> $DIR/variadic-ffi-1.rs:21:9
+ --> $DIR/variadic-ffi-1.rs:23:9
|
LL | foo(1);
| ^^^--- an argument of type `u8` is missing
|
note: function defined here
- --> $DIR/variadic-ffi-1.rs:13:8
+ --> $DIR/variadic-ffi-1.rs:15:8
|
LL | fn foo(f: isize, x: u8, ...);
| ^^^
| ~~~~~~~~~~~~~
error[E0308]: mismatched types
- --> $DIR/variadic-ffi-1.rs:23:56
+ --> $DIR/variadic-ffi-1.rs:25:56
|
LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
| ------------------------------------- ^^^ expected non-variadic fn, found variadic function
found fn item `unsafe extern "C" fn(_, _, ...) {foo}`
error[E0308]: mismatched types
- --> $DIR/variadic-ffi-1.rs:24:54
+ --> $DIR/variadic-ffi-1.rs:26:54
|
LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
| ----------------------------------- ^^^ expected variadic fn, found non-variadic function
found fn item `extern "C" fn(_, _) {bar}`
error[E0617]: can't pass `f32` to variadic function
- --> $DIR/variadic-ffi-1.rs:26:19
+ --> $DIR/variadic-ffi-1.rs:28:19
|
LL | foo(1, 2, 3f32);
| ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
error[E0617]: can't pass `bool` to variadic function
- --> $DIR/variadic-ffi-1.rs:27:19
+ --> $DIR/variadic-ffi-1.rs:29:19
|
LL | foo(1, 2, true);
| ^^^^ help: cast the value to `c_int`: `true as c_int`
error[E0617]: can't pass `i8` to variadic function
- --> $DIR/variadic-ffi-1.rs:28:19
+ --> $DIR/variadic-ffi-1.rs:30:19
|
LL | foo(1, 2, 1i8);
| ^^^ help: cast the value to `c_int`: `1i8 as c_int`
error[E0617]: can't pass `u8` to variadic function
- --> $DIR/variadic-ffi-1.rs:29:19
+ --> $DIR/variadic-ffi-1.rs:31:19
|
LL | foo(1, 2, 1u8);
| ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
error[E0617]: can't pass `i16` to variadic function
- --> $DIR/variadic-ffi-1.rs:30:19
+ --> $DIR/variadic-ffi-1.rs:32:19
|
LL | foo(1, 2, 1i16);
| ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
error[E0617]: can't pass `u16` to variadic function
- --> $DIR/variadic-ffi-1.rs:31:19
+ --> $DIR/variadic-ffi-1.rs:33:19
|
LL | foo(1, 2, 1u16);
| ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`
// ignore-arm stdcall isn't supported
+#![feature(extended_varargs_abi_support)]
+#![feature(abi_efiapi)]
fn baz(f: extern "stdcall" fn(usize, ...)) {
- //~^ ERROR: variadic function must have C or cdecl calling convention
+ //~^ ERROR: C-variadic function must have a compatible calling convention,
+ // like C, cdecl, win64, sysv64 or efiapi
+ f(22, 44);
+}
+
+fn sysv(f: extern "sysv64" fn(usize, ...)) {
+ f(22, 44);
+}
+fn win(f: extern "win64" fn(usize, ...)) {
+ f(22, 44);
+}
+fn efiapi(f: extern "efiapi" fn(usize, ...)) {
f(22, 44);
}
-error[E0045]: C-variadic function must have C or cdecl calling convention
- --> $DIR/variadic-ffi-2.rs:3:11
+error[E0045]: C-variadic function must have a compatible calling convention, like C, cdecl, win64, sysv64 or efiapi
+ --> $DIR/variadic-ffi-2.rs:5:11
|
LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error: aborting due to previous error
-error[E0045]: C-variadic function must have C or cdecl calling convention
+error[E0045]: C-variadic function must have a compatible calling convention, like C or cdecl
--> $DIR/E0045.rs:1:17
|
LL | extern "Rust" { fn foo(x: u8, ...); }
- | ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
+ | ^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error: aborting due to previous error