From fe7af948338c5221c4f28a0ef6a071efd683ebac Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 17 Feb 2026 19:33:27 +0100 Subject: [PATCH 1/6] add c-variadic function definitions --- src/items/functions.md | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/items/functions.md b/src/items/functions.md index 362b39b573..4a7676fec1 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -332,6 +332,69 @@ Note that this behavior is a consequence of the desugaring to a function that re Unsafe is used on an async function in precisely the same way that it is used on other functions: it indicates that the function imposes some additional obligations on its caller to ensure soundness. As in any other unsafe function, these conditions may extend beyond the initial call itself -- in the snippet above, for example, the `unsafe_example` function took a pointer `x` as argument, and then (when awaited) dereferenced that pointer. This implies that `x` would have to be valid until the future is finished executing, and it is the caller's responsibility to ensure that. +r[items.fn.c-variadic] +## C-variadic functions + +r[items.fn.c-variadic.intro] +A *c-variadic* function accepts a variable argument list `pat: ...` as its final parameter. + +```rust +unsafe extern "C" fn example(arg0: i32, ap: ...) { } +``` + +This parameter stands in for an arbitrary number of arguments that may be passed by the caller. + +> [!WARNING] +> Passing an unexpected number of arguments or arguments of unexpected type to a variadic function may lead to [undefined behavior][undefined]. + +r[items.fn.async.desugar-brief] +A c-variadic function definition is roughly equivalent to a function operating on a [`VaList`]. + + + +```rust +// Source +unsafe extern "C" fn example(mut ap: ...) -> i32 { + unsafe { ap.arg::() } +} +``` + +is roughly equivalent to: + +```rust +# use std::ffi::VaList; +# use std::mem::MaybeUninit; +# fn va_start() {} +// Desugared +unsafe extern "C" fn example() -> i32 { + let mut storage = MaybeUninit::>::uninit(); + va_start(storage.as_mut_ptr()); // Initializes the VaList. + let mut ap: &mut VaList<'_> = ap.assume_init_mut(); + + unsafe { ap.arg::() } + + va_end(ap) +} +``` + +r[items.fn.c-variadic.lifetime] +The lifetime of a `VaList` is that of the function that created it. Hence, the `VaList` value can never outlive the function that created it. + +r[items.fn.c-variadic.ffi-compatibility] +The rust [`VaList`] is ABI-compatible with the C `va_list` type. + +r[items.fn.c-variadic.abi] +Only `extern "C"` and `extern "C-unwind"` functions can accept a variable argument list. + +r[items.fn.c-variadic.safety] +Only `unsafe` functions can accept a variable argument list. + +r[items.fn.c-variadic.async] +A c-variadic functions cannot be `async` + +r[items.fn.c-variadic.const] +A c-variadic functions cannot be `const` + r[items.fn.attributes] ## Attributes on functions @@ -427,3 +490,4 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [variadic function]: external-blocks.md#variadic-functions [`extern` block]: external-blocks.md [zero-sized]: glossary.zst +[`VaList`]: std::ffi::VaList From 4d20c3b4f0a356819d15599c6b4f9f03f21c04fd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 18 Feb 2026 12:30:14 +0100 Subject: [PATCH 2/6] add 2 more details --- src/items/functions.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/items/functions.md b/src/items/functions.md index 4a7676fec1..95ac2f7de7 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -395,6 +395,20 @@ A c-variadic functions cannot be `async` r[items.fn.c-variadic.const] A c-variadic functions cannot be `const` +r[items.fn.c-variadic.platform-support] +Some ABIs do not support c-variadic function definitions. The compiler errors in this case. + +``` +error: the `bpfel` target does not support c-variadic functions + --> $DIR/not-supported.rs:23:31 + | +LL | unsafe extern "C" fn variadic(_: ...) {} + | ^^^^^^ +``` + +r[items.fn.c-variadic.dyn-compat] +When a trait method is c-variadic, the trait is no longer dyn-compatible. + r[items.fn.attributes] ## Attributes on functions From 3f29b0935e69a907e16b468dc5161b862ea0d324 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 3 Mar 2026 21:31:43 +0100 Subject: [PATCH 3/6] changes after code review --- src/items/functions.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/items/functions.md b/src/items/functions.md index 95ac2f7de7..73412843a9 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -83,7 +83,7 @@ r[items.fn.params.self-restriction] Functions with a self parameter may only appear as an [associated function] in a [trait] or [implementation]. r[items.fn.params.varargs] -A parameter with the `...` token indicates a [variadic function], and may only be used as the last parameter of an [external block] function. The variadic parameter may have an optional identifier, such as `args: ...`. +A parameter with the `...` token indicates a [c-variadic function]. The variadic parameter may have an optional identifier, such as `args: ...`. r[items.fn.body] ## Function body @@ -339,7 +339,9 @@ r[items.fn.c-variadic.intro] A *c-variadic* function accepts a variable argument list `pat: ...` as its final parameter. ```rust -unsafe extern "C" fn example(arg0: i32, ap: ...) { } +unsafe extern "C" fn example(ap: ...) -> f64 { + unsafe { ap.arg::() } +} ``` This parameter stands in for an arbitrary number of arguments that may be passed by the caller. @@ -347,7 +349,10 @@ This parameter stands in for an arbitrary number of arguments that may be passed > [!WARNING] > Passing an unexpected number of arguments or arguments of unexpected type to a variadic function may lead to [undefined behavior][undefined]. -r[items.fn.async.desugar-brief] +r[items.fn.c-variadic.variadic-parameter-type] +The type of `pat` in the function body is [`VaList`]. + +r[items.fn.c-variadic.desugar-brief] A c-variadic function definition is roughly equivalent to a function operating on a [`VaList`]. @@ -501,7 +506,7 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [associated function]: associated-items.md#associated-functions-and-methods [implementation]: implementations.md [value namespace]: ../names/namespaces.md -[variadic function]: external-blocks.md#variadic-functions +[c-variadic function]: external-blocks.md#variadic-functions [`extern` block]: external-blocks.md [zero-sized]: glossary.zst [`VaList`]: std::ffi::VaList From 0807e681dc87de388f92b31d9d369fd0d25ca3c0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 23 Apr 2026 17:27:18 +0200 Subject: [PATCH 4/6] tweak and simplify --- src/items/functions.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/items/functions.md b/src/items/functions.md index 73412843a9..10dac781c4 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -83,7 +83,7 @@ r[items.fn.params.self-restriction] Functions with a self parameter may only appear as an [associated function] in a [trait] or [implementation]. r[items.fn.params.varargs] -A parameter with the `...` token indicates a [c-variadic function]. The variadic parameter may have an optional identifier, such as `args: ...`. +A parameter with the `...` token indicates a [c-variadic function], and may only be used as the last parameter. In an [`extern` block] the c-variadic parameter may have an optional identifier, such as `args: ...`, in a [c-variadic function definition] the identifier is mandatory. r[items.fn.body] ## Function body @@ -340,27 +340,25 @@ A *c-variadic* function accepts a variable argument list `pat: ...` as its final ```rust unsafe extern "C" fn example(ap: ...) -> f64 { - unsafe { ap.arg::() } + unsafe { ap.next_arg::() } } ``` This parameter stands in for an arbitrary number of arguments that may be passed by the caller. > [!WARNING] -> Passing an unexpected number of arguments or arguments of unexpected type to a variadic function may lead to [undefined behavior][undefined]. +> Passing an unexpected number of arguments or arguments of unexpected type to a c-variadic function may lead to [undefined behavior][undefined]. -r[items.fn.c-variadic.variadic-parameter-type] +r[items.fn.c-variadic.c-variadic-parameter-type] The type of `pat` in the function body is [`VaList`]. r[items.fn.c-variadic.desugar-brief] A c-variadic function definition is roughly equivalent to a function operating on a [`VaList`]. - - ```rust // Source unsafe extern "C" fn example(mut ap: ...) -> i32 { - unsafe { ap.arg::() } + unsafe { ap.next_arg::() } } ``` @@ -368,15 +366,11 @@ is roughly equivalent to: ```rust # use std::ffi::VaList; -# use std::mem::MaybeUninit; -# fn va_start() {} // Desugared unsafe extern "C" fn example() -> i32 { - let mut storage = MaybeUninit::>::uninit(); - va_start(storage.as_mut_ptr()); // Initializes the VaList. - let mut ap: &mut VaList<'_> = ap.assume_init_mut(); + let mut ap: VaList<'_> = /* compiler initializes the VaList */; - unsafe { ap.arg::() } + unsafe { ap.next_arg::() } va_end(ap) } @@ -389,7 +383,7 @@ r[items.fn.c-variadic.ffi-compatibility] The rust [`VaList`] is ABI-compatible with the C `va_list` type. r[items.fn.c-variadic.abi] -Only `extern "C"` and `extern "C-unwind"` functions can accept a variable argument list. +Only `extern "C"` and `extern "C-unwind"` function defintions can accept a variable argument list. r[items.fn.c-variadic.safety] Only `unsafe` functions can accept a variable argument list. @@ -403,7 +397,7 @@ A c-variadic functions cannot be `const` r[items.fn.c-variadic.platform-support] Some ABIs do not support c-variadic function definitions. The compiler errors in this case. -``` +```text error: the `bpfel` target does not support c-variadic functions --> $DIR/not-supported.rs:23:31 | @@ -412,7 +406,7 @@ LL | unsafe extern "C" fn variadic(_: ...) {} ``` r[items.fn.c-variadic.dyn-compat] -When a trait method is c-variadic, the trait is no longer dyn-compatible. +When a trait method is c-variadic, the trait is no longer [dyn-compatible]. r[items.fn.attributes] ## Attributes on functions @@ -510,3 +504,4 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [`extern` block]: external-blocks.md [zero-sized]: glossary.zst [`VaList`]: std::ffi::VaList +[dyn-compatible]: traits.md#dyn-compatibility From 56c3a47174904ef4adf93ba48301a0a7ca085ad0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 13 May 2026 14:11:42 +0200 Subject: [PATCH 5/6] Update src/items/functions.md Co-authored-by: Tshepang Mbambo --- src/items/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/functions.md b/src/items/functions.md index 10dac781c4..5bd0027607 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -83,7 +83,7 @@ r[items.fn.params.self-restriction] Functions with a self parameter may only appear as an [associated function] in a [trait] or [implementation]. r[items.fn.params.varargs] -A parameter with the `...` token indicates a [c-variadic function], and may only be used as the last parameter. In an [`extern` block] the c-variadic parameter may have an optional identifier, such as `args: ...`, in a [c-variadic function definition] the identifier is mandatory. +A parameter with the `...` token indicates a [c-variadic function], and may only be used as the last parameter. In an [`extern` block], the c-variadic parameter may have an optional identifier, such as `args: ...`, and in a [c-variadic function definition], the identifier is mandatory. r[items.fn.body] ## Function body From 9880b05750bbcd9138815f8992cb67d15af66a25 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 13 May 2026 14:51:36 +0200 Subject: [PATCH 6/6] add rule for stable-targets --- src/items/functions.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/items/functions.md b/src/items/functions.md index 5bd0027607..1b83e5da70 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -349,6 +349,24 @@ This parameter stands in for an arbitrary number of arguments that may be passed > [!WARNING] > Passing an unexpected number of arguments or arguments of unexpected type to a c-variadic function may lead to [undefined behavior][undefined]. +[items.fn.c-variadic.stable-targets] +Support for c-variadic function definitions is stable on the following architectures: + +- x86 and x86-64 +- ARM +- AArch64 and Arm64EC +- RISC-V (except when using the ilp32e ABI) +- LoongArch +- s390x +- PowerPC and PowerPC64 +- AmdGpu and Nvptx64 +- wasm32 and wasm64 +- csky +- xtensa +- hexagon +- sparc64 +- mips + r[items.fn.c-variadic.c-variadic-parameter-type] The type of `pat` in the function body is [`VaList`].