From eb8f85e7f48cb4fdcf8ea0f203736e8579c57b6d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:00:08 +0200 Subject: [PATCH 1/4] c-variadic: document `Clone` and `Drop` instances --- library/core/src/ffi/va_list.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0a8b43622fae..fe6fae478957 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -251,6 +251,9 @@ pub(crate) const fn duplicate(&self) -> Self { #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Clone for VaList<'f> { + /// Clone the [`VaList`], producing a second independent cursor into the variable argument list. + /// + /// Corresponds to `va_copy` in C. #[inline] // Avoid codegen when not used to help backends that don't support VaList. fn clone(&self) -> Self { // We only implement Clone and not Copy because some future target might not be able to @@ -263,8 +266,14 @@ fn clone(&self) -> Self { #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Drop for VaList<'f> { + /// Drop the [`VaList`]. + /// + /// Corresponds to `va_end` in C. #[inline] // Avoid codegen when not used to help backends that don't support VaList. fn drop(&mut self) { + // Call the rust `va_end` intrinsic, which is a no-op and does not map to LLVM `va_end`. + // The rust intrinsic exists as a hook for Miri to check for UB. + // // SAFETY: this variable argument list is being dropped, so won't be read from again. unsafe { va_end(self) } } From ab19cd3dea151cf070d504c605c618c1d05b91e9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:00:51 +0200 Subject: [PATCH 2/4] c-variadic: add a `Copy` bound to `VaArgSafe` because a VaList can be cloned, the same c-variadic argument can be read twice and that is only safe if the argument type is copy --- library/core/src/ffi/va_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index fe6fae478957..afd166d4931e 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -333,7 +333,7 @@ impl Sealed for *const T {} // types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used // to accept unsupported types in the meantime. #[lang = "va_arg_safe"] -pub unsafe trait VaArgSafe: sealed::Sealed {} +pub unsafe trait VaArgSafe: Copy + sealed::Sealed {} crate::cfg_select! { any(target_arch = "avr", target_arch = "msp430") => { From ba9444c4f48c56b8351eace4f1b9e93b41083aba Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:01:18 +0200 Subject: [PATCH 3/4] c-variadic: test that const and mutable void and char pointers implement `VaArgSafe` --- library/core/src/ffi/va_list.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index afd166d4931e..a27f9e2deec9 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -390,6 +390,12 @@ const fn va_arg_safe_check() {} va_arg_safe_check::(); va_arg_safe_check::(); + + va_arg_safe_check::<*const crate::ffi::c_void>(); + va_arg_safe_check::<*mut crate::ffi::c_void>(); + + va_arg_safe_check::<*const crate::ffi::c_char>(); + va_arg_safe_check::<*mut crate::ffi::c_char>(); }; impl<'f> VaList<'f> { From ac12c696b806d7a8a5e06ca212b1469a24976ded Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 30 Apr 2026 20:32:15 +0200 Subject: [PATCH 4/4] remove custom `va_end` implementation in the LLVM backend it should use the fallback body instead --- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 3 ++- tests/codegen-llvm/c-variadic-va-end.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/c-variadic-va-end.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f4a5e8baa2a5..aa144558211e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -148,8 +148,9 @@ pub fn codegen_intrinsic_call( return Ok(()); } + // va_end uses the fallback body (a no-op). sym::va_start => bx.va_start(args[0].immediate()), - sym::va_end => bx.va_end(args[0].immediate()), + sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); diff --git a/tests/codegen-llvm/c-variadic-va-end.rs b/tests/codegen-llvm/c-variadic-va-end.rs new file mode 100644 index 000000000000..b0d7371ba01c --- /dev/null +++ b/tests/codegen-llvm/c-variadic-va-end.rs @@ -0,0 +1,19 @@ +//@ add-minicore +//@ compile-flags: -Copt-level=3 +#![feature(c_variadic)] +#![crate_type = "lib"] + +unsafe extern "C" { + fn g(v: *mut u8); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn f(mut args: ...) { + // CHECK: call void @llvm.va_start + unsafe { g(&raw mut args as *mut u8) } + // We expect one call to the LLVM va_end from our desugaring of `...`. The `Drop` implementation + // should only call the rust va_end intrinsic, which is a no-op. + // + // CHECK: call void @llvm.va_end + // CHECK-NOT: call void @llvm.va_end +}