Rework c_variadic

This commit is contained in:
beetrees 2025-07-04 17:44:15 +01:00 committed by Folkert de Vries
parent 672388edbe
commit f7b3c1d3c0
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
22 changed files with 591 additions and 291 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,15 @@
#[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.
//
// Most targets explicitly specify the layout of `va_list`, this layout is matched here.
// For `va_list`s which are single-element array in C (and therefore experience array-to-pointer
// decay when passed as arguments in C), the `VaList` struct is annotated with
// `#[rustc_pass_indirectly_in_non_rustic_abis]`. This 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",
@ -27,66 +27,60 @@ crate::cfg_select! {
/// 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`.
#[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`.
#[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`.
#[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`.
#[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>,
}
}
@ -95,94 +89,32 @@ crate::cfg_select! {
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
// - windows
// - 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 +135,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 +170,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 +190,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 +204,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

@ -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)]
@ -25,23 +25,23 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
}
// 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.
// removing the "spoofed" `VaList` 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)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %0)
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)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %0, [[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)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %0, [[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)
// CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %0, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
}

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:26:1
|
LL | pub extern "C" fn take_va_list(_: VaList<'_>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,46 @@
//@ 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 dosen't use `#[rustc_pass_indirectly_in_non_rustic_abis]` and is tested in CI, so is here
// for comparison.
//@ [win] only-windows
#![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:26: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:26: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:36: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:43: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