diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f4a5e8baa2a5f..aa144558211ef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -148,8 +148,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 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/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0a8b43622faef..a27f9e2deec94 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -251,6 +251,9 @@ impl VaList<'_> { #[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 @@ impl<'f> const Clone for VaList<'f> { #[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) } } @@ -324,7 +333,7 @@ mod sealed { // 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") => { @@ -381,6 +390,12 @@ const _: () = { 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> { 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 0000000000000..b0d7371ba01c2 --- /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 +}