about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_passes/messages.ftl2
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs7
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs7
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs3
-rw-r--r--tests/ui/c-variadic/inherent-method.rs45
-rw-r--r--tests/ui/c-variadic/not-async.rs10
-rw-r--r--tests/ui/c-variadic/not-async.stderr30
-rw-r--r--tests/ui/c-variadic/not-dyn-compatible.rs35
-rw-r--r--tests/ui/c-variadic/not-dyn-compatible.stderr21
-rw-r--r--tests/ui/c-variadic/trait-method.rs73
-rw-r--r--tests/ui/c-variadic/unsupported-abi.rs123
-rw-r--r--tests/ui/c-variadic/unsupported-abi.stderr345
-rw-r--r--tests/ui/feature-gates/feature-gate-c_variadic.rs1
-rw-r--r--tests/ui/feature-gates/feature-gate-c_variadic.stderr8
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.rs18
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.stderr36
17 files changed, 721 insertions, 49 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index e5405a7ad91..5e10c5a77d9 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -64,8 +64,6 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
 
 ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
 
-ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list
-
 ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
     .label = `extern "{$abi}"` because of this
     .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index dc221c2fb1a..f773b02058e 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -696,7 +696,7 @@ impl<'a> AstValidator<'a> {
 
         match fn_ctxt {
             FnCtxt::Foreign => return,
-            FnCtxt::Free => match sig.header.ext {
+            FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext {
                 Extern::Implicit(_) => {
                     if !matches!(sig.header.safety, Safety::Unsafe(_)) {
                         self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
@@ -726,11 +726,6 @@ impl<'a> AstValidator<'a> {
                     self.dcx().emit_err(err);
                 }
             },
-            FnCtxt::Assoc(_) => {
-                // For now, C variable argument lists are unsupported in associated functions.
-                let err = errors::CVariadicAssociatedFunction { span: variadic_param.span };
-                self.dcx().emit_err(err);
-            }
         }
     }
 
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index e09ca5b81c8..fd75e999d13 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -319,13 +319,6 @@ pub(crate) struct ExternItemAscii {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_c_variadic_associated_function)]
-pub(crate) struct CVariadicAssociatedFunction {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(ast_passes_c_variadic_no_extern)]
 #[help]
 pub(crate) struct CVariadicNoExtern {
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index ab8a3142953..0c7bddf60d9 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -823,6 +823,9 @@ impl DynCompatibilityViolation {
             DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => {
                 format!("method `{name}` is `async`").into()
             }
+            DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => {
+                format!("method `{name}` is C-variadic").into()
+            }
             DynCompatibilityViolation::Method(
                 name,
                 MethodViolationCode::WhereClauseReferencesSelf,
@@ -977,6 +980,9 @@ pub enum MethodViolationCode {
     /// e.g., `fn foo<A>()`
     Generic,
 
+    /// e.g., `fn (mut ap: ...)`
+    CVariadic,
+
     /// the method's receiver (`self` argument) can't be dispatched on
     UndispatchableReceiver(Option<Span>),
 }
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index bcd11d6918d..3260dd712b9 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -426,6 +426,9 @@ fn virtual_call_violations_for_method<'tcx>(
     if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
         errors.push(code);
     }
+    if sig.skip_binder().c_variadic {
+        errors.push(MethodViolationCode::CVariadic);
+    }
 
     // We can't monomorphize things like `fn foo<A>(...)`.
     let own_counts = tcx.generics_of(method.def_id).own_counts();
diff --git a/tests/ui/c-variadic/inherent-method.rs b/tests/ui/c-variadic/inherent-method.rs
new file mode 100644
index 00000000000..537bae7b3f0
--- /dev/null
+++ b/tests/ui/c-variadic/inherent-method.rs
@@ -0,0 +1,45 @@
+//@ run-pass
+#![feature(c_variadic)]
+
+#[repr(transparent)]
+struct S(i32);
+
+impl S {
+    unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
+        unsafe { ap.arg() }
+    }
+
+    unsafe extern "C" fn method_owned(self, mut ap: ...) -> i32 {
+        self.0 + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn method_ref(&self, mut ap: ...) -> i32 {
+        self.0 + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn method_mut(&mut self, mut ap: ...) -> i32 {
+        self.0 + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn fat_pointer(self: Box<Self>, mut ap: ...) -> i32 {
+        self.0 + unsafe { ap.arg::<i32>() }
+    }
+}
+
+fn main() {
+    unsafe {
+        assert_eq!(S::associated_function(32), 32);
+        assert_eq!(S(100).method_owned(32), 132);
+        assert_eq!(S(100).method_ref(32), 132);
+        assert_eq!(S(100).method_mut(32), 132);
+        assert_eq!(S::fat_pointer(Box::new(S(100)), 32), 132);
+
+        type Method<T> = unsafe extern "C" fn(T, ...) -> i32;
+
+        assert_eq!((S::associated_function as unsafe extern "C" fn(...) -> i32)(32), 32);
+        assert_eq!((S::method_owned as Method<_>)(S(100), 32), 132);
+        assert_eq!((S::method_ref as Method<_>)(&S(100), 32), 132);
+        assert_eq!((S::method_mut as Method<_>)(&mut S(100), 32), 132);
+        assert_eq!((S::fat_pointer as Method<_>)(Box::new(S(100)), 32), 132);
+    }
+}
diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs
index 45a7e1f8972..bdb51a9a432 100644
--- a/tests/ui/c-variadic/not-async.rs
+++ b/tests/ui/c-variadic/not-async.rs
@@ -2,6 +2,14 @@
 #![feature(c_variadic)]
 #![crate_type = "lib"]
 
-async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
+async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
 //~^ ERROR functions cannot be both `async` and C-variadic
 //~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
+
+struct S;
+
+impl S {
+    async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
+    //~^ ERROR functions cannot be both `async` and C-variadic
+    //~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
+}
diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr
index b8caf0d8bd8..eab4c4f0822 100644
--- a/tests/ui/c-variadic/not-async.stderr
+++ b/tests/ui/c-variadic/not-async.stderr
@@ -1,19 +1,35 @@
 error: functions cannot be both `async` and C-variadic
   --> $DIR/not-async.rs:5:1
    |
-LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
-   | ^^^^^ `async` because of this                        ^^^ C-variadic because of this
+LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
+   | ^^^^^ `async` because of this                           ^^^ C-variadic because of this
+
+error: functions cannot be both `async` and C-variadic
+  --> $DIR/not-async.rs:12:5
+   |
+LL |     async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
+   |     ^^^^^ `async` because of this                               ^^^ C-variadic because of this
 
 error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
-  --> $DIR/not-async.rs:5:59
+  --> $DIR/not-async.rs:5:62
    |
-LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {}
-   | --------------------------------------------------------- ^^
+LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {}
+   | ------------------------------------------------------------ ^^
    | |
    | opaque type defined here
    |
-   = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_`
+   = note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_`
+
+error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
+  --> $DIR/not-async.rs:12:70
+   |
+LL |     async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {}
+   |     ---------------------------------------------------------------- ^^
+   |     |
+   |     opaque type defined here
+   |
+   = note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_`
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/c-variadic/not-dyn-compatible.rs b/tests/ui/c-variadic/not-dyn-compatible.rs
new file mode 100644
index 00000000000..b40a13e5847
--- /dev/null
+++ b/tests/ui/c-variadic/not-dyn-compatible.rs
@@ -0,0 +1,35 @@
+// Traits where a method is c-variadic are not dyn compatible.
+//
+// Creating a function pointer from a method on an `&dyn T` value creates a ReifyShim.
+// This shim cannot reliably forward C-variadic arguments. Thus the trait as a whole
+// is dyn-incompatible to prevent invalid shims from being created.
+#![feature(c_variadic)]
+
+#[repr(transparent)]
+struct Struct(u64);
+
+trait Trait {
+    fn get(&self) -> u64;
+
+    unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 {
+        self.get() + unsafe { ap.arg::<u64>() }
+    }
+}
+
+impl Trait for Struct {
+    fn get(&self) -> u64 {
+        self.0
+    }
+}
+
+fn main() {
+    unsafe {
+        let dyn_object: &dyn Trait = &Struct(64);
+        //~^ ERROR the trait `Trait` is not dyn compatible
+        assert_eq!(dyn_object.dyn_method_ref(100), 164);
+        assert_eq!(
+            (Trait::dyn_method_ref as unsafe extern "C" fn(_, ...) -> u64)(dyn_object, 100),
+            164
+        );
+    }
+}
diff --git a/tests/ui/c-variadic/not-dyn-compatible.stderr b/tests/ui/c-variadic/not-dyn-compatible.stderr
new file mode 100644
index 00000000000..76630600c51
--- /dev/null
+++ b/tests/ui/c-variadic/not-dyn-compatible.stderr
@@ -0,0 +1,21 @@
+error[E0038]: the trait `Trait` is not dyn compatible
+  --> $DIR/not-dyn-compatible.rs:27:30
+   |
+LL |         let dyn_object: &dyn Trait = &Struct(64);
+   |                              ^^^^^ `Trait` is not dyn compatible
+   |
+note: for a trait to be dyn compatible it needs to allow building a vtable
+      for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
+  --> $DIR/not-dyn-compatible.rs:14:26
+   |
+LL | trait Trait {
+   |       ----- this trait is not dyn compatible...
+...
+LL |     unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 {
+   |                          ^^^^^^^^^^^^^^ ...because method `dyn_method_ref` is C-variadic
+   = help: consider moving `dyn_method_ref` to another trait
+   = help: only type `Struct` implements `Trait`; consider using it directly instead.
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0038`.
diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs
new file mode 100644
index 00000000000..97da0706a3a
--- /dev/null
+++ b/tests/ui/c-variadic/trait-method.rs
@@ -0,0 +1,73 @@
+//@ run-pass
+#![feature(c_variadic)]
+
+#[repr(transparent)]
+struct Struct(i32);
+
+impl Struct {
+    unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
+        unsafe { ap.arg() }
+    }
+
+    unsafe extern "C" fn method(&self, mut ap: ...) -> i32 {
+        self.0 + unsafe { ap.arg::<i32>() }
+    }
+}
+
+trait Trait: Sized {
+    fn get(&self) -> i32;
+
+    unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
+        unsafe { ap.arg() }
+    }
+
+    unsafe extern "C" fn trait_method_owned(self, mut ap: ...) -> i32 {
+        self.get() + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn trait_method_ref(&self, mut ap: ...) -> i32 {
+        self.get() + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn trait_method_mut(&mut self, mut ap: ...) -> i32 {
+        self.get() + unsafe { ap.arg::<i32>() }
+    }
+
+    unsafe extern "C" fn trait_fat_pointer(self: Box<Self>, mut ap: ...) -> i32 {
+        self.get() + unsafe { ap.arg::<i32>() }
+    }
+}
+
+impl Trait for Struct {
+    fn get(&self) -> i32 {
+        self.0
+    }
+}
+
+fn main() {
+    unsafe {
+        assert_eq!(Struct::associated_function(32), 32);
+        assert_eq!(Struct(100).method(32), 132);
+
+        assert_eq!(Struct::trait_associated_function(32), 32);
+        assert_eq!(Struct(100).trait_method_owned(32), 132);
+        assert_eq!(Struct(100).trait_method_ref(32), 132);
+        assert_eq!(Struct(100).trait_method_mut(32), 132);
+        assert_eq!(Struct::trait_fat_pointer(Box::new(Struct(100)), 32), 132);
+
+        assert_eq!(<Struct as Trait>::trait_associated_function(32), 32);
+        assert_eq!(Trait::trait_method_owned(Struct(100), 32), 132);
+        assert_eq!(Trait::trait_method_ref(&Struct(100), 32), 132);
+        assert_eq!(Trait::trait_method_mut(&mut Struct(100), 32), 132);
+        assert_eq!(Trait::trait_fat_pointer(Box::new(Struct(100)), 32), 132);
+
+        type Associated = unsafe extern "C" fn(...) -> i32;
+        type Method<T> = unsafe extern "C" fn(T, ...) -> i32;
+
+        assert_eq!((Struct::trait_associated_function as Associated)(32), 32);
+        assert_eq!((Struct::trait_method_owned as Method<_>)(Struct(100), 32), 132);
+        assert_eq!((Struct::trait_method_ref as Method<_>)(&Struct(100), 32), 132);
+        assert_eq!((Struct::trait_method_mut as Method<_>)(&mut Struct(100), 32), 132);
+        assert_eq!((Struct::trait_fat_pointer as Method<_>)(Box::new(Struct(100)), 32), 132);
+    }
+}
diff --git a/tests/ui/c-variadic/unsupported-abi.rs b/tests/ui/c-variadic/unsupported-abi.rs
new file mode 100644
index 00000000000..40179b164ce
--- /dev/null
+++ b/tests/ui/c-variadic/unsupported-abi.rs
@@ -0,0 +1,123 @@
+//@ add-core-stubs
+//@ needs-llvm-components: x86
+//@ compile-flags: --target=i686-pc-windows-gnu --crate-type=rlib
+#![no_core]
+#![feature(no_core, lang_items, c_variadic)]
+
+// Test that ABIs for which C-variadics are not supported report an error.
+
+extern crate minicore;
+use minicore::*;
+
+#[rustfmt::skip]
+mod foreign {
+    extern "Rust"  { fn rust_foreign_explicit(_: ...); }
+    //~^ ERROR C-variadic functions with the "Rust" calling convention are not supported
+    extern "C"  { fn c_foreign(_: ...); }
+    extern "C-unwind"  { fn c_unwind_foreign(_: ...); }
+    extern "cdecl"  { fn cdecl_foreign(_: ...); }
+    extern "cdecl-unwind"  { fn cdecl_unwind_foreign(_: ...); }
+    extern "stdcall"  { fn stdcall_foreign(_: ...); }
+    //~^ ERROR C-variadic functions with the "stdcall" calling convention are not supported
+    extern "stdcall-unwind"  { fn stdcall_unwind_foreign(_: ...); }
+    //~^ ERROR C-variadic functions with the "stdcall-unwind" calling convention are not supported
+    extern "thiscall"  { fn thiscall_foreign(_: ...); }
+    //~^ ERROR C-variadic functions with the "thiscall" calling convention are not supported
+    extern "thiscall-unwind"  { fn thiscall_unwind_foreign(_: ...); }
+    //~^ ERROR C-variadic functions with the "thiscall-unwind" calling convention are not supported
+}
+
+#[lang = "va_list"]
+struct VaList(*mut u8);
+
+unsafe fn rust_free(_: ...) {}
+//~^ ERROR `...` is not supported for non-extern functions
+unsafe extern "Rust" fn rust_free_explicit(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "Rust"` functions
+
+unsafe extern "C" fn c_free(_: ...) {}
+unsafe extern "C-unwind" fn c_unwind_free(_: ...) {}
+
+unsafe extern "cdecl" fn cdecl_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "cdecl"` functions
+unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions
+unsafe extern "stdcall" fn stdcall_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "stdcall"` functions
+unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions
+unsafe extern "thiscall" fn thiscall_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "thiscall"` functions
+unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {}
+//~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions
+
+struct S;
+
+impl S {
+    unsafe fn rust_method(_: ...) {}
+    //~^ ERROR `...` is not supported for non-extern functions
+    unsafe extern "Rust" fn rust_method_explicit(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "Rust"` functions
+
+    unsafe extern "C" fn c_method(_: ...) {}
+    unsafe extern "C-unwind" fn c_unwind_method(_: ...) {}
+
+    unsafe extern "cdecl" fn cdecl_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl"` functions
+    unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions
+    unsafe extern "stdcall" fn stdcall_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall"` functions
+    unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions
+    unsafe extern "thiscall" fn thiscall_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall"` functions
+    unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions
+}
+
+trait T {
+    unsafe fn rust_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for non-extern functions
+    unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "Rust"` functions
+
+    unsafe extern "C" fn c_trait_method(_: ...) {}
+    unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {}
+
+    unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl"` functions
+    unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions
+    unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall"` functions
+    unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions
+    unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall"` functions
+    unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions
+}
+
+impl T for S {
+    unsafe fn rust_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for non-extern functions
+    unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "Rust"` functions
+
+    unsafe extern "C" fn c_trait_method(_: ...) {}
+    unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {}
+
+    unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl"` functions
+    unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions
+    unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall"` functions
+    unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions
+    unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall"` functions
+    unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {}
+    //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions
+}
diff --git a/tests/ui/c-variadic/unsupported-abi.stderr b/tests/ui/c-variadic/unsupported-abi.stderr
new file mode 100644
index 00000000000..daed9171406
--- /dev/null
+++ b/tests/ui/c-variadic/unsupported-abi.stderr
@@ -0,0 +1,345 @@
+error: `...` is not supported for non-extern functions
+  --> $DIR/unsupported-abi.rs:33:21
+   |
+LL | unsafe fn rust_free(_: ...) {}
+   |                     ^^^^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "Rust"` functions
+  --> $DIR/unsupported-abi.rs:35:44
+   |
+LL | unsafe extern "Rust" fn rust_free_explicit(_: ...) {}
+   |        -------------                       ^^^^^^
+   |        |
+   |        `extern "Rust"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl"` functions
+  --> $DIR/unsupported-abi.rs:41:37
+   |
+LL | unsafe extern "cdecl" fn cdecl_free(_: ...) {}
+   |        --------------               ^^^^^^
+   |        |
+   |        `extern "cdecl"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl-unwind"` functions
+  --> $DIR/unsupported-abi.rs:43:51
+   |
+LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {}
+   |        ---------------------                      ^^^^^^
+   |        |
+   |        `extern "cdecl-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall"` functions
+  --> $DIR/unsupported-abi.rs:45:41
+   |
+LL | unsafe extern "stdcall" fn stdcall_free(_: ...) {}
+   |        ----------------                 ^^^^^^
+   |        |
+   |        `extern "stdcall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:47:55
+   |
+LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {}
+   |        -----------------------                        ^^^^^^
+   |        |
+   |        `extern "stdcall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall"` functions
+  --> $DIR/unsupported-abi.rs:49:43
+   |
+LL | unsafe extern "thiscall" fn thiscall_free(_: ...) {}
+   |        -----------------                  ^^^^^^
+   |        |
+   |        `extern "thiscall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:51:57
+   |
+LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {}
+   |        ------------------------                         ^^^^^^
+   |        |
+   |        `extern "thiscall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for non-extern functions
+  --> $DIR/unsupported-abi.rs:57:27
+   |
+LL |     unsafe fn rust_method(_: ...) {}
+   |                           ^^^^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "Rust"` functions
+  --> $DIR/unsupported-abi.rs:59:50
+   |
+LL |     unsafe extern "Rust" fn rust_method_explicit(_: ...) {}
+   |            -------------                         ^^^^^^
+   |            |
+   |            `extern "Rust"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl"` functions
+  --> $DIR/unsupported-abi.rs:65:43
+   |
+LL |     unsafe extern "cdecl" fn cdecl_method(_: ...) {}
+   |            --------------                 ^^^^^^
+   |            |
+   |            `extern "cdecl"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl-unwind"` functions
+  --> $DIR/unsupported-abi.rs:67:57
+   |
+LL |     unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {}
+   |            ---------------------                        ^^^^^^
+   |            |
+   |            `extern "cdecl-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall"` functions
+  --> $DIR/unsupported-abi.rs:69:47
+   |
+LL |     unsafe extern "stdcall" fn stdcall_method(_: ...) {}
+   |            ----------------                   ^^^^^^
+   |            |
+   |            `extern "stdcall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:71:61
+   |
+LL |     unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {}
+   |            -----------------------                          ^^^^^^
+   |            |
+   |            `extern "stdcall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall"` functions
+  --> $DIR/unsupported-abi.rs:73:49
+   |
+LL |     unsafe extern "thiscall" fn thiscall_method(_: ...) {}
+   |            -----------------                    ^^^^^^
+   |            |
+   |            `extern "thiscall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:75:63
+   |
+LL |     unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {}
+   |            ------------------------                           ^^^^^^
+   |            |
+   |            `extern "thiscall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for non-extern functions
+  --> $DIR/unsupported-abi.rs:80:33
+   |
+LL |     unsafe fn rust_trait_method(_: ...) {}
+   |                                 ^^^^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "Rust"` functions
+  --> $DIR/unsupported-abi.rs:82:56
+   |
+LL |     unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {}
+   |            -------------                               ^^^^^^
+   |            |
+   |            `extern "Rust"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl"` functions
+  --> $DIR/unsupported-abi.rs:88:49
+   |
+LL |     unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {}
+   |            --------------                       ^^^^^^
+   |            |
+   |            `extern "cdecl"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl-unwind"` functions
+  --> $DIR/unsupported-abi.rs:90:63
+   |
+LL |     unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {}
+   |            ---------------------                              ^^^^^^
+   |            |
+   |            `extern "cdecl-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall"` functions
+  --> $DIR/unsupported-abi.rs:92:53
+   |
+LL |     unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {}
+   |            ----------------                         ^^^^^^
+   |            |
+   |            `extern "stdcall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:94:67
+   |
+LL |     unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {}
+   |            -----------------------                                ^^^^^^
+   |            |
+   |            `extern "stdcall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall"` functions
+  --> $DIR/unsupported-abi.rs:96:55
+   |
+LL |     unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {}
+   |            -----------------                          ^^^^^^
+   |            |
+   |            `extern "thiscall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:98:69
+   |
+LL |     unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {}
+   |            ------------------------                                 ^^^^^^
+   |            |
+   |            `extern "thiscall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for non-extern functions
+  --> $DIR/unsupported-abi.rs:103:33
+   |
+LL |     unsafe fn rust_trait_method(_: ...) {}
+   |                                 ^^^^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "Rust"` functions
+  --> $DIR/unsupported-abi.rs:105:56
+   |
+LL |     unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {}
+   |            -------------                               ^^^^^^
+   |            |
+   |            `extern "Rust"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl"` functions
+  --> $DIR/unsupported-abi.rs:111:49
+   |
+LL |     unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {}
+   |            --------------                       ^^^^^^
+   |            |
+   |            `extern "cdecl"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "cdecl-unwind"` functions
+  --> $DIR/unsupported-abi.rs:113:63
+   |
+LL |     unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {}
+   |            ---------------------                              ^^^^^^
+   |            |
+   |            `extern "cdecl-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall"` functions
+  --> $DIR/unsupported-abi.rs:115:53
+   |
+LL |     unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {}
+   |            ----------------                         ^^^^^^
+   |            |
+   |            `extern "stdcall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "stdcall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:117:67
+   |
+LL |     unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {}
+   |            -----------------------                                ^^^^^^
+   |            |
+   |            `extern "stdcall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall"` functions
+  --> $DIR/unsupported-abi.rs:119:55
+   |
+LL |     unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {}
+   |            -----------------                          ^^^^^^
+   |            |
+   |            `extern "thiscall"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error: `...` is not supported for `extern "thiscall-unwind"` functions
+  --> $DIR/unsupported-abi.rs:121:69
+   |
+LL |     unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {}
+   |            ------------------------                                 ^^^^^^
+   |            |
+   |            `extern "thiscall-unwind"` because of this
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
+
+error[E0045]: C-variadic functions with the "Rust" calling convention are not supported
+  --> $DIR/unsupported-abi.rs:14:22
+   |
+LL |     extern "Rust"  { fn rust_foreign_explicit(_: ...); }
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0045]: C-variadic functions with the "stdcall" calling convention are not supported
+  --> $DIR/unsupported-abi.rs:20:25
+   |
+LL |     extern "stdcall"  { fn stdcall_foreign(_: ...); }
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0045]: C-variadic functions with the "stdcall-unwind" calling convention are not supported
+  --> $DIR/unsupported-abi.rs:22:32
+   |
+LL |     extern "stdcall-unwind"  { fn stdcall_unwind_foreign(_: ...); }
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0045]: C-variadic functions with the "thiscall" calling convention are not supported
+  --> $DIR/unsupported-abi.rs:24:26
+   |
+LL |     extern "thiscall"  { fn thiscall_foreign(_: ...); }
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error[E0045]: C-variadic functions with the "thiscall-unwind" calling convention are not supported
+  --> $DIR/unsupported-abi.rs:26:33
+   |
+LL |     extern "thiscall-unwind"  { fn thiscall_unwind_foreign(_: ...); }
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+
+error: aborting due to 37 previous errors
+
+For more information about this error, try `rustc --explain E0045`.
diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs
index 88d91dbd081..649816b48d7 100644
--- a/tests/ui/feature-gates/feature-gate-c_variadic.rs
+++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs
@@ -6,5 +6,4 @@ pub unsafe extern "C" fn test(_: i32, ap: ...) {}
 trait Trait {
     unsafe extern "C" fn trait_test(_: i32, ap: ...) {}
     //~^ ERROR C-variadic functions are unstable
-    //~| ERROR associated functions cannot have a C variable argument list
 }
diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr
index 808aa20948d..ae880093b98 100644
--- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr
+++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr
@@ -1,9 +1,3 @@
-error: associated functions cannot have a C variable argument list
-  --> $DIR/feature-gate-c_variadic.rs:7:45
-   |
-LL |     unsafe extern "C" fn trait_test(_: i32, ap: ...) {}
-   |                                             ^^^^^^^
-
 error[E0658]: C-variadic functions are unstable
   --> $DIR/feature-gate-c_variadic.rs:3:1
    |
@@ -24,6 +18,6 @@ LL |     unsafe extern "C" fn trait_test(_: i32, ap: ...) {}
    = help: add `#![feature(c_variadic)]` 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 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
index 4db056f15a5..415472176d9 100644
--- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
+++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
@@ -53,30 +53,30 @@ struct X;
 
 impl X {
     fn i_f1(x: isize, ...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn i_f2(...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn i_f3(..., x: isize, ...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     //~| ERROR `...` must be the last argument of a C-variadic function
     fn i_f4(..., x: isize, ...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     //~| ERROR `...` must be the last argument of a C-variadic function
     const fn i_f5(x: isize, ...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     //~| ERROR functions cannot be both `const` and C-variadic
     //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
 }
 
 trait T {
     fn t_f1(x: isize, ...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn t_f2(x: isize, ...);
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn t_f3(...) {}
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn t_f4(...);
-    //~^ ERROR associated functions cannot have a C variable argument list
+    //~^ ERROR `...` is not supported for non-extern functions
     fn t_f5(..., x: isize) {}
     //~^ ERROR `...` must be the last argument of a C-variadic function
     fn t_f6(..., x: isize);
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
index 0cd78318de6..da9c9b0f760 100644
--- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
+++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
@@ -132,17 +132,21 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn e_f2(..., x: isize);
    |             ^^^
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23
    |
 LL |     fn i_f1(x: isize, ...) {}
    |                       ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13
    |
 LL |     fn i_f2(...) {}
    |             ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 error: `...` must be the last argument of a C-variadic function
   --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13
@@ -150,11 +154,13 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn i_f3(..., x: isize, ...) {}
    |             ^^^
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28
    |
 LL |     fn i_f3(..., x: isize, ...) {}
    |                            ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 error: `...` must be the last argument of a C-variadic function
   --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13
@@ -162,11 +168,13 @@ error: `...` must be the last argument of a C-variadic function
 LL |     fn i_f4(..., x: isize, ...) {}
    |             ^^^
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28
    |
 LL |     fn i_f4(..., x: isize, ...) {}
    |                            ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 error: functions cannot be both `const` and C-variadic
   --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5
@@ -176,35 +184,45 @@ LL |     const fn i_f5(x: isize, ...) {}
    |     |
    |     `const` because of this
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29
    |
 LL |     const fn i_f5(x: isize, ...) {}
    |                             ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23
    |
 LL |     fn t_f1(x: isize, ...) {}
    |                       ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23
    |
 LL |     fn t_f2(x: isize, ...);
    |                       ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13
    |
 LL |     fn t_f3(...) {}
    |             ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
-error: associated functions cannot have a C variable argument list
+error: `...` is not supported for non-extern functions
   --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13
    |
 LL |     fn t_f4(...);
    |             ^^^
+   |
+   = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
 
 error: `...` must be the last argument of a C-variadic function
   --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13