]> git.lizzy.rs Git - rust.git/commitdiff
Enable varargs support for calling conventions other than C or cdecl
authorSoveu <marx.tomasz@gmail.com>
Mon, 8 Aug 2022 13:31:32 +0000 (15:31 +0200)
committerJack Huey <31162821+jackh726@users.noreply.github.com>
Sun, 23 Oct 2022 22:46:16 +0000 (18:46 -0400)
This patch makes it possible to use varargs for calling conventions,
which are either based on C (like efiapi) or C is based
on them (for example sysv64 and win64).

12 files changed:
compiler/rustc_feature/src/active.rs
compiler/rustc_hir_analysis/src/lib.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_target/src/spec/abi.rs
src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md [new file with mode: 0644]
src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs [new file with mode: 0644]
src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr [new file with mode: 0644]
src/test/ui/c-variadic/variadic-ffi-1.rs
src/test/ui/c-variadic/variadic-ffi-1.stderr
src/test/ui/c-variadic/variadic-ffi-2.rs
src/test/ui/c-variadic/variadic-ffi-2.stderr
src/test/ui/error-codes/E0045.stderr

index 1b8d683b13361a407b61efca99071af4748324d6..0f6561b04c0ce083e74feaae2f9e129759ae7cbd 100644 (file)
@@ -390,6 +390,9 @@ pub fn set(&self, features: &mut Features, span: Span) {
     (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.
index dba505149de87f2a3e12a85b57e460e4c772a730..782c95d6335553ca32dd7522502290dafc79f7db 100644 (file)
 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>(
index 3fe79370c374d378929d12cabec1a847c675af86..4a1b20297d9d8df0b72166f1c7ad720cf76b1398 100644 (file)
         export_name,
         expr,
         extended_key_value_attributes,
+        extended_varargs_abi_support,
         extern_absolute_paths,
         extern_crate_item_prelude,
         extern_crate_self,
index ce45fa13970b4bdfa4f4b25c38100d0e4ffa3381..cb2a0c04c6aa8ba1f82af68cd23ac67742afe4de 100644 (file)
@@ -40,6 +40,28 @@ pub enum Abi {
     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,
diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md
new file mode 100644 (file)
index 0000000..b20c30e
--- /dev/null
@@ -0,0 +1,10 @@
+# `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.
diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
new file mode 100644 (file)
index 0000000..e391ee8
--- /dev/null
@@ -0,0 +1,19 @@
+#![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() {}
diff --git a/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/src/test/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
new file mode 100644 (file)
index 0000000..3442d53
--- /dev/null
@@ -0,0 +1,49 @@
+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`.
index a76efd9a205088efdbd729ca7bc77c46454399f7..24407a71ce6997184531b3c1018f6bf474efbbf1 100644 (file)
@@ -6,7 +6,9 @@
 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" {
index 2ffb80f7ef6149c34dd547484318f72eaccbd3d1..f9d6928b3df144cf01cc89c441587dfc42b62500 100644 (file)
@@ -1,17 +1,17 @@
-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, ...);
    |        ^^^
@@ -21,13 +21,13 @@ LL |         foo(/* isize */, /* 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, ...);
    |        ^^^
@@ -37,7 +37,7 @@ LL |         foo(1, /* 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
@@ -48,7 +48,7 @@ LL |         let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
                  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
@@ -59,37 +59,37 @@ LL |         let y: extern "C" fn(f: isize, x: u8, ...) = bar;
                  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`
index 224ac16f4586abefac49b224a727cf22f09a0faa..96cea87546e7ad3b3fa1a1f21937a9b3e6a7dae7 100644 (file)
@@ -1,7 +1,20 @@
 // 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);
 }
 
index 4c8b8d2b2e1a960bf81069868517a72ac7f6cf67..117d75301fb7e2cc4251069bc2fa15569d9d7047 100644 (file)
@@ -1,8 +1,8 @@
-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
 
index d163128bc8b6c0851cefef63cfd77bb30bf96c2d..ecb916d02df52b9b62411a09f98ea714f8e9a051 100644 (file)
@@ -1,8 +1,8 @@
-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