Auto merge of #141980 - beetrees:va-list-proposal, r=workingjubilee

`c_variadic`: make `VaList` abi-compatible with C

tracking issue: https://github.com/rust-lang/rust/issues/44930
related PR: rust-lang/rust#144529

On some platforms, the C `va_list` type is actually a single-element array of a struct (on other platforms it is just a pointer). In C, arrays passed as function arguments expirience array-to-pointer decay, which means that C will pass a pointer to the array in the caller instead of the array itself, and modifications to the array in the callee will be visible to the caller (this does not match Rust by-value semantics). However, for `va_list`, the C standard explicitly states that it is undefined behaviour to use a `va_list` after it has been passed by value to a function (in Rust parlance, the `va_list` is moved, not copied). This matches Rust's pass-by-value semantics, meaning that when the C `va_list` type is a single-element array of a struct, the ABI will match C as long as the Rust type is always be passed indirectly.

In the old implementation, this ABI was achieved by having two separate types: `VaList` was the type that needed to be used when passing a `VaList` as a function parameter, whereas `VaListImpl` was the actual `va_list` type that was correct everywhere else. This however is quite confusing, as there are lots of footguns: it is easy to cause bugs by mixing them up (e.g. the C function `void foo(va_list va)` was equivalent to the Rust `fn foo(va: VaList)` whereas the C function `void bar(va_list* va)` was equivalent to the Rust `fn foo(va: *mut VaListImpl)`, not `fn foo(va: *mut VaList)` as might be expected); also converting from `VaListImpl` to `VaList` with `as_va_list()` had platform specific behaviour: on single-element array of a struct platforms it would return a `VaList` referencing the original `VaListImpl`, whereas on other platforms it would return a cioy,

In this PR, there is now just a single `VaList` type (renamed from `VaListImpl`) which represents the C `va_list` type and will just work in all positions. Instead of having a separate type just to make the ABI work, rust-lang/rust#144529 adds a `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute, which when applied to a struct will force the struct to be passed indirectly by non-Rustic calling conventions. This PR then implements the `VaList` rework, making use of the new attribute on all platforms where the C `va_list` type is a single-element array of a struct.

Cleanup of the `VaList` API and implementation is also included in this PR: since it was decided it was OK to experiment with Rust requiring that not calling `va_end` is not undefined behaviour (https://github.com/rust-lang/rust/issues/141524#issuecomment-3028383594), I've removed the `with_copy` method as it was redundant to the `Clone` impl (the `Drop` impl of `VaList` is a no-op as `va_end` is a no-op on all known platforms).

Previous discussion: rust-lang/rust#141524 and [t-compiler > c_variadic API and ABI](https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/c_variadic.20API.20and.20ABI)
Tracking issue: https://github.com/rust-lang/rust/issues/44930
r? `@joshtriplett`
This commit is contained in:
bors 2025-12-05 23:36:55 +00:00
commit 36b2369c91
23 changed files with 667 additions and 311 deletions

View file

@ -36,10 +36,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
vtable_byte_offset: u64,
typeid: Self::Metadata,
) -> Self::Value;
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
/// Trait method used to inject `va_start` on the "spoofed" `VaList` in
/// Rust defined C-variadic functions.
fn va_start(&mut self, val: Self::Value) -> Self::Value;
/// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before
/// Trait method used to inject `va_end` on the "spoofed" `VaList` before
/// Rust defined C-variadic functions return.
fn va_end(&mut self, val: Self::Value) -> Self::Value;
}

View file

@ -28,7 +28,7 @@ pub mod c_str;
issue = "44930",
reason = "the `c_variadic` feature has not been properly tested on all supported platforms"
)]
pub use self::va_list::{VaArgSafe, VaList, VaListImpl};
pub use self::va_list::{VaArgSafe, VaList};
#[unstable(
feature = "c_variadic",

View file

@ -4,15 +4,33 @@
#[cfg(not(target_arch = "xtensa"))]
use crate::ffi::c_void;
#[allow(unused_imports)]
use crate::fmt;
use crate::intrinsics::{va_arg, va_copy, va_end};
use crate::marker::{PhantomData, PhantomInvariantLifetime};
use crate::ops::{Deref, DerefMut};
use crate::intrinsics::{va_arg, va_copy};
use crate::marker::PhantomCovariantLifetime;
// The name is WIP, using `VaListImpl` for now.
// There are currently three flavors of how a C `va_list` is implemented for
// targets that Rust supports:
//
// Most targets explicitly specify the layout of `va_list`, this layout is matched here.
// - `va_list` is an opaque pointer
// - `va_list` is a struct
// - `va_list` is a single-element array, containing a struct
//
// The opaque pointer approach is the simplest to implement: the pointer just
// points to an array of arguments on the caller's stack.
//
// The struct and single-element array variants are more complex, but
// potentially more efficient because the additional state makes it
// possible to pass variadic arguments via registers.
//
// The Rust `VaList` type is ABI-compatible with the C `va_list`.
// The struct and pointer cases straightforwardly map to their Rust equivalents,
// but the single-element array case is special: in C, this type is subject to
// array-to-pointer decay.
//
// The `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute is used to match
// the pointer decay behavior in Rust, while otherwise matching Rust semantics.
// This attribute ensures that the compiler uses the correct ABI for functions
// like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly.
crate::cfg_select! {
all(
target_arch = "aarch64",
@ -20,73 +38,89 @@ crate::cfg_select! {
not(target_os = "uefi"),
not(windows),
) => {
/// AArch64 ABI implementation of a `va_list`. See the
/// [AArch64 Procedure Call Standard] for more details.
/// AArch64 ABI implementation of a `va_list`.
///
/// See the [AArch64 Procedure Call Standard] for more details.
///
/// [AArch64 Procedure Call Standard]:
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
#[repr(C)]
#[derive(Debug)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
stack: *mut c_void,
gr_top: *mut c_void,
vr_top: *mut c_void,
struct VaListInner {
stack: *const c_void,
gr_top: *const c_void,
vr_top: *const c_void,
gr_offs: i32,
vr_offs: i32,
_marker: PhantomInvariantLifetime<'f>,
}
}
all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => {
/// PowerPC ABI implementation of a `va_list`.
///
/// See the [LLVM source] and [GCC header] for more details.
///
/// [LLVM source]:
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111
/// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h
#[repr(C)]
#[derive(Debug)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
#[rustc_pass_indirectly_in_non_rustic_abis]
struct VaListInner {
gpr: u8,
fpr: u8,
reserved: u16,
overflow_arg_area: *mut c_void,
reg_save_area: *mut c_void,
_marker: PhantomInvariantLifetime<'f>,
overflow_arg_area: *const c_void,
reg_save_area: *const c_void,
}
}
target_arch = "s390x" => {
/// s390x ABI implementation of a `va_list`.
///
/// See the [S/390x ELF Application Binary Interface Supplement] for more details.
///
/// [S/390x ELF Application Binary Interface Supplement]:
/// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf
#[repr(C)]
#[derive(Debug)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
#[rustc_pass_indirectly_in_non_rustic_abis]
struct VaListInner {
gpr: i64,
fpr: i64,
overflow_arg_area: *mut c_void,
reg_save_area: *mut c_void,
_marker: PhantomInvariantLifetime<'f>,
overflow_arg_area: *const c_void,
reg_save_area: *const c_void,
}
}
all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => {
/// x86_64 ABI implementation of a `va_list`.
/// x86_64 System V ABI implementation of a `va_list`.
///
/// See the [System V AMD64 ABI] for more details.
///
/// [System V AMD64 ABI]:
/// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
#[repr(C)]
#[derive(Debug)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
#[rustc_pass_indirectly_in_non_rustic_abis]
struct VaListInner {
gp_offset: i32,
fp_offset: i32,
overflow_arg_area: *mut c_void,
reg_save_area: *mut c_void,
_marker: PhantomInvariantLifetime<'f>,
overflow_arg_area: *const c_void,
reg_save_area: *const c_void,
}
}
target_arch = "xtensa" => {
/// Xtensa ABI implementation of a `va_list`.
///
/// See the [LLVM source] for more details.
///
/// [LLVM source]:
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215
#[repr(C)]
#[derive(Debug)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
stk: *mut i32,
reg: *mut i32,
#[rustc_pass_indirectly_in_non_rustic_abis]
struct VaListInner {
stk: *const i32,
reg: *const i32,
ndx: i32,
_marker: PhantomInvariantLifetime<'f>,
}
}
@ -94,95 +128,34 @@ crate::cfg_select! {
//
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
// - windows
// - powerpc64 & powerpc64le
// - uefi
// - any other target for which we don't specify the `VaListImpl` above
// - any other target for which we don't specify the `VaListInner` above
//
// In this implementation the `va_list` type is just an alias for an opaque pointer.
// That pointer is probably just the next variadic argument on the caller's stack.
_ => {
/// Basic implementation of a `va_list`.
#[repr(transparent)]
#[lang = "va_list"]
pub struct VaListImpl<'f> {
ptr: *mut c_void,
// Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
// the region of the function it's defined in
_marker: PhantomInvariantLifetime<'f>,
}
impl<'f> fmt::Debug for VaListImpl<'f> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "va_list* {:p}", self.ptr)
}
}
}
}
crate::cfg_select! {
all(
any(
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "s390x",
target_arch = "x86_64"
),
not(target_arch = "xtensa"),
any(not(target_arch = "aarch64"), not(target_vendor = "apple")),
not(target_family = "wasm"),
not(target_os = "uefi"),
not(windows),
) => {
/// A wrapper for a `va_list`
#[repr(transparent)]
#[derive(Debug)]
pub struct VaList<'a, 'f: 'a> {
inner: &'a mut VaListImpl<'f>,
_marker: PhantomData<&'a mut VaListImpl<'f>>,
}
impl<'f> VaListImpl<'f> {
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
#[inline]
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
VaList { inner: self, _marker: PhantomData }
}
}
}
_ => {
/// A wrapper for a `va_list`
#[repr(transparent)]
#[derive(Debug)]
pub struct VaList<'a, 'f: 'a> {
inner: VaListImpl<'f>,
_marker: PhantomData<&'a mut VaListImpl<'f>>,
}
impl<'f> VaListImpl<'f> {
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
#[inline]
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
}
struct VaListInner {
ptr: *const c_void,
}
}
}
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
type Target = VaListImpl<'f>;
#[inline]
fn deref(&self) -> &VaListImpl<'f> {
&self.inner
}
/// A variable argument list, equivalent to `va_list` in C.
#[repr(transparent)]
#[lang = "va_list"]
pub struct VaList<'a> {
inner: VaListInner,
_marker: PhantomCovariantLifetime<'a>,
}
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
#[inline]
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
&mut self.inner
impl fmt::Debug for VaList<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// No need to include `_marker` in debug output.
f.debug_tuple("VaList").field(&self.inner).finish()
}
}
@ -203,7 +176,7 @@ mod sealed {
impl<T> Sealed for *const T {}
}
/// Types that are valid to read using [`VaListImpl::arg`].
/// Types that are valid to read using [`VaList::arg`].
///
/// # Safety
///
@ -238,7 +211,7 @@ unsafe impl VaArgSafe for f64 {}
unsafe impl<T> VaArgSafe for *mut T {}
unsafe impl<T> VaArgSafe for *const T {}
impl<'f> VaListImpl<'f> {
impl<'f> VaList<'f> {
/// Advance to and read the next variable argument.
///
/// # Safety
@ -258,27 +231,13 @@ impl<'f> VaListImpl<'f> {
// SAFETY: the caller must uphold the safety contract for `va_arg`.
unsafe { va_arg(self) }
}
/// Copies the `va_list` at the current location.
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
where
F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
{
let mut ap = self.clone();
let ret = f(ap.as_va_list());
// SAFETY: the caller must uphold the safety contract for `va_end`.
unsafe {
va_end(&mut ap);
}
ret
}
}
impl<'f> Clone for VaListImpl<'f> {
impl<'f> Clone for VaList<'f> {
#[inline]
fn clone(&self) -> Self {
let mut dest = crate::mem::MaybeUninit::uninit();
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
unsafe {
va_copy(dest.as_mut_ptr(), self);
dest.assume_init()
@ -286,18 +245,11 @@ impl<'f> Clone for VaListImpl<'f> {
}
}
impl<'f> Drop for VaListImpl<'f> {
impl<'f> Drop for VaList<'f> {
fn drop(&mut self) {
// FIXME: this should call `va_end`, but there's no clean way to
// guarantee that `drop` always gets inlined into its caller,
// so the `va_end` would get directly called from the same function as
// the corresponding `va_copy`. `man va_end` states that C requires this,
// and LLVM basically follows the C semantics, so we need to make sure
// that `va_end` is always called from the same function as `va_copy`.
// For more details, see https://github.com/rust-lang/rust/pull/59625
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
//
// This works for now, since `va_end` is a no-op on all current LLVM targets.
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
// destructor is empty.
}
}

View file

@ -54,7 +54,7 @@
)]
#![allow(missing_docs)]
use crate::ffi::va_list::{VaArgSafe, VaListImpl};
use crate::ffi::va_list::{VaArgSafe, VaList};
use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple};
use crate::{mem, ptr};
@ -3447,7 +3447,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
///
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
/// argument `ap` points to.
@ -3465,7 +3465,7 @@ pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
///
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`.
///
@ -3475,4 +3475,4 @@ pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
///
#[rustc_intrinsic]
#[rustc_nounwind]
pub unsafe fn va_end(ap: &mut VaListImpl<'_>);
pub unsafe fn va_end(ap: &mut VaList<'_>);

View file

@ -172,7 +172,7 @@ pub use core::ffi::c_void;
all supported platforms",
issue = "44930"
)]
pub use core::ffi::{VaArgSafe, VaList, VaListImpl};
pub use core::ffi::{VaArgSafe, VaList};
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub use core::ffi::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,

View file

@ -121,6 +121,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-thumbv8m.base-none-eabi",
"ignore-thumbv8m.main-none-eabi",
"ignore-tvos",
"ignore-uefi",
"ignore-unix",
"ignore-unknown",
"ignore-uwp",

View file

@ -314,6 +314,10 @@ double rust_interesting_average(uint64_t n, ...) {
return sum;
}
int32_t rust_va_list_next_i32(va_list* ap) {
return va_arg(*ap, int32_t);
}
int32_t rust_int8_to_int32(int8_t x) {
return (int32_t)x;
}

View file

@ -1,4 +1,4 @@
// Tests that `VaListImpl::clone` gets inlined into a call to `llvm.va_copy`
// Tests that `VaList::clone` gets inlined into a call to `llvm.va_copy`
#![crate_type = "lib"]
#![feature(c_variadic)]
@ -12,5 +12,5 @@ extern "C" {
pub unsafe extern "C" fn clone_variadic(ap: VaList) {
let mut ap2 = ap.clone();
// CHECK: call void @llvm.va_copy
foreign_c_variadic_1(ap2.as_va_list(), 42i32);
foreign_c_variadic_1(ap2, 42i32);
}

View file

@ -10,21 +10,21 @@ extern "C" {
}
// Ensure that `va_start` and `va_end` are properly injected even
// when the "spoofed" `VaListImpl` is not used.
// when the "spoofed" `VaList` is not used.
#[no_mangle]
pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 {
// CHECK: call void @llvm.va_start
vprintf(fmt, ap.as_va_list())
vprintf(fmt, ap)
// CHECK: call void @llvm.va_end
}
// Check that `VaListImpl::clone` gets inlined into a direct call to `llvm.va_copy`
// Check that `VaList::clone` gets inlined into a direct call to `llvm.va_copy`
#[no_mangle]
pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 {
// CHECK: call void @llvm.va_start
let mut ap2 = ap.clone();
// CHECK: call void @llvm.va_copy
let res = vprintf(fmt, ap2.as_va_list());
let res = vprintf(fmt, ap2);
res
// CHECK: call void @llvm.va_end
}

View file

@ -0,0 +1,36 @@
//@ needs-unwind
//@ compile-flags: -Copt-level=3
//@ min-llvm-version: 21
#![crate_type = "lib"]
#![feature(c_variadic)]
#![no_std]
use core::ffi::VaList;
// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaList` that is used by Rust defined C-variadics.
extern "C" {
fn foreign_c_variadic_1(_: VaList, ...);
}
// CHECK-LABEL: use_foreign_c_variadic_1_0
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap)
foreign_c_variadic_1(ap);
}
// CHECK-LABEL: use_foreign_c_variadic_1_1
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 42)
foreign_c_variadic_1(ap, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42)
foreign_c_variadic_1(ap, 2i32, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42, i32 noundef 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

View file

@ -1,6 +1,6 @@
//@ needs-unwind
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0
//
//@ min-llvm-version: 21
#![crate_type = "lib"]
#![feature(c_variadic)]
@ -9,7 +9,6 @@ use core::ffi::VaList;
extern "C" {
fn foreign_c_variadic_0(_: i32, ...);
fn foreign_c_variadic_1(_: VaList, ...);
}
pub unsafe extern "C" fn use_foreign_c_variadic_0() {
@ -24,27 +23,6 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
foreign_c_variadic_0(0, 42i32, 1024i32, 0i32);
}
// Ensure that we do not remove the `va_list` passed to the foreign function when
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap)
foreign_c_variadic_1(ap);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
foreign_c_variadic_1(ap, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
foreign_c_variadic_1(ap, 2i32, 42i32);
}
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}
// Ensure that `va_start` and `va_end` are properly injected.
#[no_mangle]
pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 {

View file

@ -2,7 +2,7 @@
#![feature(c_variadic)]
#![feature(cfg_select)]
use std::ffi::{CStr, CString, VaList, VaListImpl, c_char, c_double, c_int, c_long, c_longlong};
use std::ffi::{CStr, CString, VaList, c_char, c_double, c_int, c_long, c_longlong};
macro_rules! continue_if {
($cond:expr) => {
@ -58,11 +58,8 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_int>() == 16);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!"));
ap.with_copy(
|mut ap| {
if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff }
},
)
let mut ap = ap.clone();
if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff }
}
#[unsafe(no_mangle)]
@ -153,8 +150,8 @@ pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize {
unsafe extern "C" {
fn test_variadic(_: c_int, ...) -> usize;
fn test_va_list_by_value(_: VaList) -> usize;
fn test_va_list_by_pointer(_: *mut VaListImpl) -> usize;
fn test_va_list_by_pointer_pointer(_: *mut *mut VaListImpl) -> usize;
fn test_va_list_by_pointer(_: *mut VaList) -> usize;
fn test_va_list_by_pointer_pointer(_: *mut *mut VaList) -> usize;
}
#[unsafe(no_mangle)]
@ -165,7 +162,7 @@ extern "C" fn run_test_variadic() -> usize {
#[unsafe(no_mangle)]
extern "C" fn run_test_va_list_by_value() -> usize {
unsafe extern "C" fn helper(mut ap: ...) -> usize {
unsafe { test_va_list_by_value(ap.as_va_list()) }
unsafe { test_va_list_by_value(ap) }
}
unsafe { helper(1 as c_longlong, 2 as c_int, 3 as c_longlong) }

View file

@ -10,37 +10,45 @@ extern "C" {
fn rust_interesting_average(_: u64, ...) -> f64;
fn rust_valist_interesting_average(_: u64, _: VaList) -> f64;
fn rust_va_list_next_i32(_: *mut VaList<'_>) -> i32;
}
pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 {
rust_valist_interesting_average(n, ap.as_va_list())
pub unsafe extern "C" fn test_valist_forward(n: u64, ap: ...) -> f64 {
rust_valist_interesting_average(n, ap)
}
pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, mut ap: ...) -> f64 {
rust_valist_interesting_average(n, ap.as_va_list())
pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, ap: ...) -> f64 {
rust_valist_interesting_average(n, ap)
}
pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {
let mut ap2 = ap.clone();
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30);
let ap2 = ap.clone();
assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 30);
// Advance one pair in the copy before checking
let mut ap2 = ap.clone();
let _ = ap2.arg::<u64>();
let _ = ap2.arg::<f64>();
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50);
// Advance one pair in the original
let _ = ap.arg::<u64>();
let _ = ap.arg::<f64>();
let mut ap2 = ap.clone();
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50);
let ap2 = ap.clone();
assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50);
let mut ap2 = ap.clone();
let _ = ap2.arg::<u64>();
let _ = ap2.arg::<f64>();
assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70);
assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 70);
}
pub unsafe extern "C" fn test_ref(mut ap: ...) {
assert_eq!(rust_va_list_next_i32(&mut ap), 2);
assert_eq!(rust_va_list_next_i32(&mut ap), 4);
assert_eq!(rust_va_list_next_i32(&mut ap), 8);
}
pub fn main() {
@ -85,4 +93,8 @@ pub fn main() {
unsafe {
test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64);
}
unsafe {
test_ref(2, 4, 8);
}
}

View file

@ -0,0 +1,80 @@
error: fn_abi_of(take_va_list) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: VaList<'_>,
layout: Layout {
size: Size(32 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: $OFFSETS,
memory_index: $MEMORY_INDEX,
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef,
arg_ext: None,
pointee_size: Size(32 bytes),
pointee_align: Some(
Align(8 bytes),
),
},
meta_attrs: None,
on_stack: false,
},
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: C,
can_unwind: false,
}
--> $DIR/pass-by-value-abi.rs:27:1
|
LL | pub extern "C" fn take_va_list(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,47 @@
//@ check-fail
//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED"
//@ normalize-stderr: "valid_range: 0\.\.=\d+" -> "valid_range: 0..=$$MAX"
//@ normalize-stderr: "memory_index: \[[^\]]+\]" -> "memory_index: $$MEMORY_INDEX"
//@ normalize-stderr: "offsets: \[[^\]]+\]" -> "offsets: $$OFFSETS"
//@ revisions: x86_64 aarch64 win
//@ compile-flags: -O
//@ [x86_64] only-x86_64
//@ [x86_64] ignore-windows
//@ [x86_64] ignore-uefi
//@ [aarch64] only-aarch64
//@ [aarch64] ignore-windows
//@ [aarch64] ignore-apple
//@ [aarch64] ignore-uefi
// Windows doesn't use `#[rustc_pass_indirectly_in_non_rustic_abis]` and is tested in CI, so is here
// for comparison.
//@ [win] only-windows
//@ [win] only-x86_64
#![feature(rustc_attrs, c_variadic)]
#![crate_type = "lib"]
// Can't use `minicore` here as this is testing the implementation in `core::ffi` specifically.
use std::ffi::VaList;
#[rustc_abi(debug)]
pub extern "C" fn take_va_list(_: VaList<'_>) {}
//~^ ERROR fn_abi_of(take_va_list) = FnAbi {
//[x86_64]~^^ ERROR mode: Indirect {
//[x86_64]~^^^ ERROR on_stack: false,
//[aarch64]~^^^^ ERROR mode: Indirect {
//[aarch64]~^^^^^ ERROR on_stack: false,
//[win]~^^^^^^ ERROR mode: Direct(
#[cfg(all(target_arch = "x86_64", not(windows)))]
#[rustc_abi(debug)]
pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {}
//[x86_64]~^ ERROR fn_abi_of(take_va_list_sysv64) = FnAbi {
//[x86_64]~^^ ERROR mode: Indirect {
//[x86_64]~^^^ ERROR on_stack: false,
#[cfg(all(target_arch = "x86_64", not(windows)))]
#[rustc_abi(debug)]
pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {}
//[x86_64]~^ ERROR: fn_abi_of(take_va_list_win64) = FnAbi {
//[x86_64]~^^ ERROR mode: Indirect {
//[x86_64]~^^^ ERROR on_stack: false,

View file

@ -0,0 +1,83 @@
error: fn_abi_of(take_va_list) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: VaList<'_>,
layout: Layout {
size: Size(8 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Scalar(
Initialized {
value: Pointer(
AddressSpace(
0,
),
),
valid_range: 0..=$MAX,
},
),
fields: Arbitrary {
offsets: $OFFSETS,
memory_index: $MEMORY_INDEX,
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
},
mode: Direct(
ArgAttributes {
regular: NoUndef,
arg_ext: None,
pointee_size: Size(0 bytes),
pointee_align: None,
},
),
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: C,
can_unwind: false,
}
--> $DIR/pass-by-value-abi.rs:27:1
|
LL | pub extern "C" fn take_va_list(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,240 @@
error: fn_abi_of(take_va_list) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: VaList<'_>,
layout: Layout {
size: Size(24 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: $OFFSETS,
memory_index: $MEMORY_INDEX,
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef,
arg_ext: None,
pointee_size: Size(24 bytes),
pointee_align: Some(
Align(8 bytes),
),
},
meta_attrs: None,
on_stack: false,
},
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: C,
can_unwind: false,
}
--> $DIR/pass-by-value-abi.rs:27:1
|
LL | pub extern "C" fn take_va_list(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: fn_abi_of(take_va_list_sysv64) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: VaList<'_>,
layout: Layout {
size: Size(24 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: $OFFSETS,
memory_index: $MEMORY_INDEX,
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef,
arg_ext: None,
pointee_size: Size(24 bytes),
pointee_align: Some(
Align(8 bytes),
),
},
meta_attrs: None,
on_stack: false,
},
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: X86(
SysV64,
),
can_unwind: false,
}
--> $DIR/pass-by-value-abi.rs:37:1
|
LL | pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: fn_abi_of(take_va_list_win64) = FnAbi {
args: [
ArgAbi {
layout: TyAndLayout {
ty: VaList<'_>,
layout: Layout {
size: Size(24 bytes),
align: AbiAlign {
abi: Align(8 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: $OFFSETS,
memory_index: $MEMORY_INDEX,
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(8 bytes),
randomization_seed: $SEED,
},
},
mode: Indirect {
attrs: ArgAttributes {
regular: CapturesAddress | NoAlias | NonNull | NoUndef,
arg_ext: None,
pointee_size: Size(24 bytes),
pointee_align: Some(
Align(8 bytes),
),
},
meta_attrs: None,
on_stack: false,
},
},
],
ret: ArgAbi {
layout: TyAndLayout {
ty: (),
layout: Layout {
size: Size(0 bytes),
align: AbiAlign {
abi: Align(1 bytes),
},
backend_repr: Memory {
sized: true,
},
fields: Arbitrary {
offsets: [],
memory_index: [],
},
largest_niche: None,
uninhabited: false,
variants: Single {
index: 0,
},
max_repr_align: None,
unadjusted_abi_align: Align(1 bytes),
randomization_seed: $SEED,
},
},
mode: Ignore,
},
c_variadic: false,
fixed_count: 1,
conv: X86(
Win64,
),
can_unwind: false,
}
--> $DIR/pass-by-value-abi.rs:44:1
|
LL | pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View file

@ -2,37 +2,30 @@
#![no_std]
#![feature(c_variadic)]
use core::ffi::{VaList, VaListImpl};
use core::ffi::VaList;
pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> {
ap
//~^ ERROR: lifetime may not live long enough
//~| ERROR: lifetime may not live long enough
}
pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> {
pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
ap //~ ERROR: lifetime may not live long enough
}
pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) {
let _ = ap.with_copy(|ap| ap); //~ ERROR: lifetime may not live long enough
}
pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
*ap0 = ap1;
//~^ ERROR: lifetime may not live long enough
//~| ERROR: lifetime may not live long enough
}
pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
ap0 = &mut ap1;
//~^ ERROR: `ap1` does not live long enough
//~| ERROR: lifetime may not live long enough
//~| ERROR: lifetime may not live long enough
}
pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
*ap0 = ap1.clone();
//~^ ERROR: lifetime may not live long enough
//~| ERROR: lifetime may not live long enough
}

View file

@ -1,113 +1,64 @@
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:8:5
|
LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
| -- -- has type `VaListImpl<'1>`
| |
| lifetime `'f` defined here
LL | ap
| ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:8:5
|
LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> {
| -- -- has type `VaListImpl<'1>`
LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> {
| -- -- has type `VaList<'1>`
| |
| lifetime `'f` defined here
LL | ap
| ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:14:5
--> $DIR/variadic-ffi-4.rs:13:5
|
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> {
| -- has type `VaListImpl<'1>`
LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> {
| -- has type `VaList<'1>`
LL | ap
| ^^ returning this value requires that `'1` must outlive `'static`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:18:31
--> $DIR/variadic-ffi-4.rs:17:5
|
LL | let _ = ap.with_copy(|ap| ap);
| --- ^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is VaList<'2, '_>
| has type `VaList<'1, '_>`
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:22:5
|
LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
| ------- ------- has type `VaList<'1>`
| |
| has type `&mut VaListImpl<'1>`
| has type `&mut VaList<'2>`
LL | *ap0 = ap1;
| ^^^^ assignment requires that `'1` must outlive `'2`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:22:5
|
LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
| ------- ------- has type `VaList<'2>`
| |
| has type `&mut VaListImpl<'1>`
LL | *ap0 = ap1;
| ^^^^ assignment requires that `'2` must outlive `'1`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:28:5
|
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
| |
| has type `&mut VaListImpl<'1>`
| has type `&mut VaList<'1>`
LL | ap0 = &mut ap1;
| ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
|
= note: requirement occurs because of a mutable reference to `VaListImpl<'_>`
= note: requirement occurs because of a mutable reference to `VaList<'_>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:28:5
--> $DIR/variadic-ffi-4.rs:22:5
|
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
| ------- ------- has type `VaList<'2>`
| |
| has type `&mut VaListImpl<'1>`
| has type `&mut VaList<'1>`
LL | ap0 = &mut ap1;
| ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1`
|
= note: requirement occurs because of a mutable reference to `VaListImpl<'_>`
= note: requirement occurs because of a mutable reference to `VaList<'_>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0597]: `ap1` does not live long enough
--> $DIR/variadic-ffi-4.rs:28:11
--> $DIR/variadic-ffi-4.rs:22:11
|
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| - ------- binding `ap1` declared here
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
| - ------- binding `ap1` declared here
| |
| let's call the lifetime of this reference `'3`
LL | ap0 = &mut ap1;
@ -120,33 +71,15 @@ LL | }
| - `ap1` dropped here while still borrowed
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:35:5
--> $DIR/variadic-ffi-4.rs:29:5
|
LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) {
| ------- ------- has type `VaList<'1>`
| |
| has type `&mut VaListImpl<'1>`
| has type `&mut VaList<'2>`
LL | *ap0 = ap1.clone();
| ^^^^ assignment requires that `'2` must outlive `'1`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
| ^^^^ assignment requires that `'1` must outlive `'2`
error: lifetime may not live long enough
--> $DIR/variadic-ffi-4.rs:35:12
|
LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
| ------- ------- has type `VaListImpl<'2>`
| |
| has type `&mut VaListImpl<'1>`
LL | *ap0 = ap1.clone();
| ^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
= note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant
= note: the struct `VaListImpl<'f>` is invariant over the parameter `'f`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to 11 previous errors
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0597`.

View file

@ -1,4 +1,4 @@
// A bare `...` represents `CVarArgs` (`VaListImpl<'_>`) in function argument type
// A bare `...` represents `CVarArgs` (`VaList<'_>`) in function argument type
// position without being a proper type syntactically.
// This test ensures that we do not regress certain MBE calls would we ever promote
// `...` to a proper type syntactically.

View file

@ -32,12 +32,12 @@ extern "C" fn f3_3(_: ..., x: isize) {}
const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
//~^ ERROR functions cannot be both `const` and C-variadic
//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
const extern "C" fn f4_2(x: isize, _: ...) {}
//~^ ERROR functions cannot be both `const` and C-variadic
//~| ERROR functions with a C variable argument list must be unsafe
//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
const extern "C" fn f4_3(_: ..., x: isize, _: ...) {}
//~^ ERROR functions cannot be both `const` and C-variadic
@ -65,7 +65,7 @@ impl X {
const fn i_f5(x: isize, _: ...) {}
//~^ 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
//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time
}
trait T {

View file

@ -236,7 +236,7 @@ error: `...` must be the last argument of a C-variadic function
LL | fn t_f6(_: ..., x: isize);
| ^^^^^^
error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
--> $DIR/variadic-ffi-semantic-restrictions.rs:33:43
|
LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
@ -244,7 +244,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {}
| |
| the destructor for this type cannot be evaluated in constant functions
error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
--> $DIR/variadic-ffi-semantic-restrictions.rs:37:36
|
LL | const extern "C" fn f4_2(x: isize, _: ...) {}
@ -252,7 +252,7 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {}
| |
| the destructor for this type cannot be evaluated in constant functions
error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time
error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time
--> $DIR/variadic-ffi-semantic-restrictions.rs:65:29
|
LL | const fn i_f5(x: isize, _: ...) {}

View file

@ -16,13 +16,13 @@ params: [
)
}
Param {
ty: std::ffi::VaListImpl<'{erased}>
ty: std::ffi::VaList<'{erased}>
ty_span: None
self_kind: None
hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).3))
param: Some(
Pat: {
ty: std::ffi::VaListImpl<'{erased}>
ty: std::ffi::VaList<'{erased}>
span: $DIR/c-variadic.rs:7:34: 7:37 (#0)
kind: PatKind {
Missing