about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-09-10 00:17:22 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2025-09-11 10:18:48 +0200
commitfd48528d185f59f60e301bce1e01d670ff4bdb30 (patch)
tree2fb3335987f19721fba3b61299552f4ddc477c70
parentf4665ab8368ad2e8a86d4390ae35c28bdd9561bb (diff)
downloadrust-fd48528d185f59f60e301bce1e01d670ff4bdb30.tar.gz
rust-fd48528d185f59f60e301bce1e01d670ff4bdb30.zip
c-variadic: allow inherent methods to be c-variadic
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs54
-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/trait-method.rs40
-rw-r--r--tests/ui/c-variadic/trait-method.stderr14
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.rs10
-rw-r--r--tests/ui/parser/variadic-ffi-semantic-restrictions.stderr20
8 files changed, 179 insertions, 44 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index a6ef89b553d..cdb3062c591 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -696,36 +696,38 @@ impl<'a> AstValidator<'a> {
 
         match fn_ctxt {
             FnCtxt::Foreign => return,
-            FnCtxt::Free => match sig.header.ext {
-                Extern::Implicit(_) => {
-                    if !matches!(sig.header.safety, Safety::Unsafe(_)) {
-                        self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
-                            span: variadic_param.span,
-                            unsafe_span: sig.safety_span(),
-                        });
-                    }
-                }
-                Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
-                    if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
-                        self.dcx().emit_err(errors::CVariadicBadExtern {
-                            span: variadic_param.span,
-                            abi: symbol_unescaped,
-                            extern_span: sig.extern_span(),
-                        });
+            FnCtxt::Free | FnCtxt::Assoc(AssocCtxt::Impl { of_trait: false }) => {
+                match sig.header.ext {
+                    Extern::Implicit(_) => {
+                        if !matches!(sig.header.safety, Safety::Unsafe(_)) {
+                            self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
+                                span: variadic_param.span,
+                                unsafe_span: sig.safety_span(),
+                            });
+                        }
                     }
+                    Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
+                        if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
+                            self.dcx().emit_err(errors::CVariadicBadExtern {
+                                span: variadic_param.span,
+                                abi: symbol_unescaped,
+                                extern_span: sig.extern_span(),
+                            });
+                        }
 
-                    if !matches!(sig.header.safety, Safety::Unsafe(_)) {
-                        self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
-                            span: variadic_param.span,
-                            unsafe_span: sig.safety_span(),
-                        });
+                        if !matches!(sig.header.safety, Safety::Unsafe(_)) {
+                            self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
+                                span: variadic_param.span,
+                                unsafe_span: sig.safety_span(),
+                            });
+                        }
+                    }
+                    Extern::None => {
+                        let err = errors::CVariadicNoExtern { span: variadic_param.span };
+                        self.dcx().emit_err(err);
                     }
                 }
-                Extern::None => {
-                    let err = errors::CVariadicNoExtern { span: variadic_param.span };
-                    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 };
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/trait-method.rs b/tests/ui/c-variadic/trait-method.rs
new file mode 100644
index 00000000000..4c468c1907b
--- /dev/null
+++ b/tests/ui/c-variadic/trait-method.rs
@@ -0,0 +1,40 @@
+// For now C-variadic arguments in trait methods are rejected, though we aim to lift this
+// restriction in the future. In particular we need to think about the interaction with
+// `dyn Trait` and the `ReifyShim`s that it may generate for methods.
+#![feature(c_variadic)]
+#![crate_type = "lib"]
+struct S;
+
+impl S {
+    unsafe extern "C" fn associated_function(mut ap: ...) -> i32 {
+        unsafe { ap.arg() }
+    }
+
+    unsafe extern "C" fn method(&self, mut ap: ...) -> i32 {
+        unsafe { ap.arg() }
+    }
+}
+
+trait T {
+    unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
+        //~^ ERROR: associated functions cannot have a C variable argument list
+        unsafe { ap.arg() }
+    }
+
+    unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 {
+        //~^ ERROR: associated functions cannot have a C variable argument list
+        unsafe { ap.arg() }
+    }
+}
+
+impl T for S {}
+
+fn main() {
+    unsafe {
+        assert_eq!(S::associated_function(32), 32);
+        assert_eq!(S.method(32), 32);
+
+        assert_eq!(S::trait_associated_function(32), 32);
+        assert_eq!(S.trait_method(32), 32);
+    }
+}
diff --git a/tests/ui/c-variadic/trait-method.stderr b/tests/ui/c-variadic/trait-method.stderr
new file mode 100644
index 00000000000..35f6e24ef71
--- /dev/null
+++ b/tests/ui/c-variadic/trait-method.stderr
@@ -0,0 +1,14 @@
+error: associated functions cannot have a C variable argument list
+  --> $DIR/trait-method.rs:19:52
+   |
+LL |     unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 {
+   |                                                    ^^^^^^^^^^^
+
+error: associated functions cannot have a C variable argument list
+  --> $DIR/trait-method.rs:24:46
+   |
+LL |     unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 {
+   |                                              ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
index 4db056f15a5..566c4d30245 100644
--- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
+++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs
@@ -53,17 +53,17 @@ 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
 }
diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr
index 0cd78318de6..c18d857d14e 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,11 +184,13 @@ 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
   --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23