From 4bb0c3da2c1397034a5036b73bc78369b719f593 Mon Sep 17 00:00:00 2001
From: Jubilee Young <workingjubilee@gmail.com>
Date: Wed, 12 Feb 2025 10:35:32 -0800
Subject: [PATCH] Split out the `extern_system_varargs` feature

After the stabilization PR was opened, `extern "system"` functions were
added to `extended_varargs_abi_support`. This has a number of questions
regarding it that were not discussed and were somewhat surprising.
It deserves to be considered as its own feature, separate from
`extended_varargs_abi_support`.
---
 compiler/rustc_abi/src/extern_abi.rs          |  2 -
 compiler/rustc_feature/src/unstable.rs        |  4 +-
 compiler/rustc_hir_analysis/src/lib.rs        | 42 ++++++++++++-------
 compiler/rustc_span/src/symbol.rs             |  1 +
 ...ature-gate-extended_varargs_abi_support.rs |  9 ++--
 ...e-gate-extended_varargs_abi_support.stderr | 27 ++----------
 tests/ui/c-variadic/variadic-ffi-1.stderr     |  2 +-
 tests/ui/c-variadic/variadic-ffi-2.rs         |  3 --
 .../cmse-nonsecure-call/generics.rs           |  2 +-
 .../cmse-nonsecure-call/generics.stderr       |  2 +-
 tests/ui/error-codes/E0045.stderr             |  2 +-
 .../feature-gate-extern_system_varargs.rs     |  7 ++++
 .../feature-gate-extern_system_varargs.stderr | 13 ++++++
 13 files changed, 63 insertions(+), 53 deletions(-)
 create mode 100644 tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
 create mode 100644 tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr

diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index c29db522511d8..543c2f8ab12cf 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -195,7 +195,6 @@ impl ExternAbi {
         // * 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 falls back to C for functions with varargs.
         //
         // * 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)
@@ -204,7 +203,6 @@ impl ExternAbi {
         match self {
             Self::C { .. }
             | Self::Cdecl { .. }
-            | Self::System { .. }
             | Self::Aapcs { .. }
             | Self::Win64 { .. }
             | Self::SysV64 { .. }
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index ee22d8990fa46..7b9273c1dd58e 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -487,9 +487,11 @@ declare_features! (
     (unstable, exhaustive_patterns, "1.13.0", Some(51085)),
     /// Allows explicit tail calls via `become` expression.
     (incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
-    /// Allows using `efiapi`, `sysv64` and `win64` as calling convention
+    /// 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, "CURRENT_RUSTC_VERSION", Some(136946)),
     /// Allows defining `extern type`s.
     (unstable, extern_types, "1.23.0", Some(43467)),
     /// Allow using 128-bit (quad precision) floating point numbers.
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 323b912ca18ad..8a529e9c686e0 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -122,28 +122,42 @@ fn require_c_abi_if_c_variadic(
     const UNSTABLE_EXPLAIN: &str =
         "using calling conventions other than `C` or `cdecl` for varargs functions is unstable";
 
+    // ABIs which can stably use varargs
     if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) {
         return;
     }
 
+    // ABIs with feature-gated stability
     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,
+    let extern_system_varargs = tcx.features().extern_system_varargs();
 
-        // 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, sym::extended_varargs_abi_support, span, UNSTABLE_EXPLAIN)
-                .emit();
-            CONVENTIONS_STABLE
-        }
-
-        (false, false) => CONVENTIONS_STABLE,
-        (true, false) => CONVENTIONS_UNSTABLE,
+    // If the feature gate has been enabled, we can stop here
+    if extern_system_varargs && let ExternAbi::System { .. } = abi {
+        return;
+    };
+    if extended_abi_support && abi.supports_varargs() {
+        return;
     };
 
-    tcx.dcx().emit_err(errors::VariadicFunctionCompatibleConvention { span, conventions });
+    // Looks like we need to pick an error to emit.
+    // Is there any feature which we could have enabled to make this work?
+    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 {
+            span,
+            conventions: if tcx.sess.opts.unstable_features.is_nightly_build() {
+                CONVENTIONS_UNSTABLE
+            } else {
+                CONVENTIONS_STABLE
+            },
+        }),
+    }
+    .emit();
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index bb59b4c40bd7f..cdc8e38970505 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -879,6 +879,7 @@ symbols! {
         extern_crate_self,
         extern_in_paths,
         extern_prelude,
+        extern_system_varargs,
         extern_types,
         external,
         external_doc,
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
index d47a8e085fd3a..58370bff2200e 100644
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
+++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs
@@ -1,18 +1,15 @@
 //@ only-x86_64
 
 fn efiapi(f: extern "efiapi" fn(usize, ...)) {
-    //~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
-    //~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: using calling conventions other 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 calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: using calling conventions other 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 calling conventions other than `C` or `cdecl` for varargs functions is unstable
+    //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
     f(22, 44);
 }
 
diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
index 41be378424543..9565575dc4279 100644
--- a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
+++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr
@@ -8,14 +8,8 @@ LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
    = help: add `#![feature(extended_varargs_abi_support)]` 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[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 calling conventions other than `C` or `cdecl` for varargs functions is unstable
-  --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
+  --> $DIR/feature-gate-extended_varargs_abi_support.rs:7:12
    |
 LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,14 +18,8 @@ LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
    = help: add `#![feature(extended_varargs_abi_support)]` 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[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 calling conventions other than `C` or `cdecl` for varargs functions is unstable
-  --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
+  --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11
    |
 LL | fn win(f: extern "win64" fn(usize, ...)) {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,13 +28,6 @@ LL | fn win(f: extern "win64" fn(usize, ...)) {
    = help: add `#![feature(extended_varargs_abi_support)]` 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[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
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0045, E0658.
-For more information about an error, try `rustc --explain E0045`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/c-variadic/variadic-ffi-1.stderr b/tests/ui/c-variadic/variadic-ffi-1.stderr
index dd8c7e93c4efe..061eae9729e2e 100644
--- a/tests/ui/c-variadic/variadic-ffi-1.stderr
+++ b/tests/ui/c-variadic/variadic-ffi-1.stderr
@@ -1,4 +1,4 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
+error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
   --> $DIR/variadic-ffi-1.rs:9:5
    |
 LL |     fn printf(_: *const u8, ...);
diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs
index bafb7e2b20cc7..99f83f22d17c0 100644
--- a/tests/ui/c-variadic/variadic-ffi-2.rs
+++ b/tests/ui/c-variadic/variadic-ffi-2.rs
@@ -8,9 +8,6 @@ fn baz(f: extern "stdcall" fn(usize, ...)) {
     f(22, 44);
 }
 
-fn system(f: extern "system" fn(usize, ...)) {
-    f(22, 44);
-}
 #[cfg(target_arch = "x86_64")]
 fn sysv(f: extern "sysv64" fn(usize, ...)) {
     f(22, 44);
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
index 9e0ffa75c2296..31782b05105f6 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
@@ -39,4 +39,4 @@ type WithTransparentTraitObject =
 //~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798]
 
 type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
-//~^ ERROR C-variadic function must have a compatible calling convention, like `C` or `cdecl` [E0045]
+//~^ ERROR C-variadic function must have a compatible calling convention, like `C`
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
index f0d5f648f4c65..0560f0eec1c02 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
@@ -69,7 +69,7 @@ LL |     extern "C-cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTranspa
    = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
+error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
   --> $DIR/generics.rs:41:20
    |
 LL | type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
diff --git a/tests/ui/error-codes/E0045.stderr b/tests/ui/error-codes/E0045.stderr
index 25b2f2654da15..b8ee31a40495a 100644
--- a/tests/ui/error-codes/E0045.stderr
+++ b/tests/ui/error-codes/E0045.stderr
@@ -1,4 +1,4 @@
-error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
+error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`
   --> $DIR/E0045.rs:1:17
    |
 LL | extern "Rust" { fn foo(x: u8, ...); }
diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
new file mode 100644
index 0000000000000..f5cfbe72ca8db
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs
@@ -0,0 +1,7 @@
+fn system(f: extern "system" fn(usize, ...)) {
+    //~^  ERROR using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+
+    f(22, 44);
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr
new file mode 100644
index 0000000000000..f3206b25264e0
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr
@@ -0,0 +1,13 @@
+error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
+  --> $DIR/feature-gate-extern_system_varargs.rs:1:14
+   |
+LL | fn system(f: extern "system" fn(usize, ...)) {
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #136946 <https://github.com/rust-lang/rust/issues/136946> for more information
+   = help: add `#![feature(extern_system_varargs)]` 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 1 previous error
+
+For more information about this error, try `rustc --explain E0658`.