Skip to content

Commit 3d5e0ce

Browse files
committed
Rework c_variadic
1 parent c0357c1 commit 3d5e0ce

File tree

21 files changed

+589
-288
lines changed

21 files changed

+589
-288
lines changed

compiler/rustc_codegen_ssa/src/traits/intrinsic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes {
3636
vtable_byte_offset: u64,
3737
typeid: Self::Metadata,
3838
) -> Self::Value;
39-
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
39+
/// Trait method used to inject `va_start` on the "spoofed" `VaList` in
4040
/// Rust defined C-variadic functions.
4141
fn va_start(&mut self, val: Self::Value) -> Self::Value;
42-
/// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before
42+
/// Trait method used to inject `va_end` on the "spoofed" `VaList` before
4343
/// Rust defined C-variadic functions return.
4444
fn va_end(&mut self, val: Self::Value) -> Self::Value;
4545
}

library/core/src/ffi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub mod c_str;
2828
issue = "44930",
2929
reason = "the `c_variadic` feature has not been properly tested on all supported platforms"
3030
)]
31-
pub use self::va_list::{VaArgSafe, VaList, VaListImpl};
31+
pub use self::va_list::{VaArgSafe, VaList};
3232

3333
#[unstable(
3434
feature = "c_variadic",

library/core/src/ffi/va_list.rs

Lines changed: 52 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
//! Better known as "varargs".
44
55
use crate::ffi::c_void;
6-
#[allow(unused_imports)]
76
use crate::fmt;
8-
use crate::intrinsics::{va_arg, va_copy, va_end};
9-
use crate::marker::{PhantomData, PhantomInvariantLifetime};
10-
use crate::ops::{Deref, DerefMut};
7+
use crate::intrinsics::{va_arg, va_copy};
8+
use crate::marker::PhantomCovariantLifetime;
119

12-
// The name is WIP, using `VaListImpl` for now.
13-
//
1410
// Most targets explicitly specify the layout of `va_list`, this layout is matched here.
11+
// For `va_list`s which are single-element array in C (and therefore experience array-to-pointer
12+
// decay when passed as arguments in C), the `VaList` struct is annotated with
13+
// `#[rustc_pass_indirectly_in_non_rustic_abis]`. This ensures that the compiler uses the correct
14+
// ABI for functions like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly.
1515
crate::cfg_select! {
1616
all(
1717
target_arch = "aarch64",
@@ -24,68 +24,63 @@ crate::cfg_select! {
2424
///
2525
/// [AArch64 Procedure Call Standard]:
2626
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
27-
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
27+
#[repr(C)]
2828
#[derive(Debug)]
29-
#[lang = "va_list"]
30-
pub struct VaListImpl<'f> {
31-
stack: *mut c_void,
32-
gr_top: *mut c_void,
33-
vr_top: *mut c_void,
29+
#[rustc_pass_indirectly_in_non_rustic_abis]
30+
struct VaListInner {
31+
stack: *const c_void,
32+
gr_top: *const c_void,
33+
vr_top: *const c_void,
3434
gr_offs: i32,
3535
vr_offs: i32,
36-
_marker: PhantomInvariantLifetime<'f>,
3736
}
3837
}
3938
all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => {
4039
/// PowerPC ABI implementation of a `va_list`.
41-
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
40+
#[repr(C)]
4241
#[derive(Debug)]
43-
#[lang = "va_list"]
44-
pub struct VaListImpl<'f> {
42+
#[rustc_pass_indirectly_in_non_rustic_abis]
43+
struct VaListInner {
4544
gpr: u8,
4645
fpr: u8,
4746
reserved: u16,
48-
overflow_arg_area: *mut c_void,
49-
reg_save_area: *mut c_void,
50-
_marker: PhantomInvariantLifetime<'f>,
47+
overflow_arg_area: *const c_void,
48+
reg_save_area: *const c_void,
5149
}
5250
}
5351
target_arch = "s390x" => {
5452
/// s390x ABI implementation of a `va_list`.
55-
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
53+
#[repr(C)]
5654
#[derive(Debug)]
57-
#[lang = "va_list"]
58-
pub struct VaListImpl<'f> {
55+
#[rustc_pass_indirectly_in_non_rustic_abis]
56+
struct VaListInner {
5957
gpr: i64,
6058
fpr: i64,
61-
overflow_arg_area: *mut c_void,
62-
reg_save_area: *mut c_void,
63-
_marker: PhantomInvariantLifetime<'f>,
59+
overflow_arg_area: *const c_void,
60+
reg_save_area: *const c_void,
6461
}
6562
}
6663
all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => {
6764
/// x86_64 ABI implementation of a `va_list`.
68-
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
65+
#[repr(C)]
6966
#[derive(Debug)]
70-
#[lang = "va_list"]
71-
pub struct VaListImpl<'f> {
67+
#[rustc_pass_indirectly_in_non_rustic_abis]
68+
struct VaListInner {
7269
gp_offset: i32,
7370
fp_offset: i32,
74-
overflow_arg_area: *mut c_void,
75-
reg_save_area: *mut c_void,
76-
_marker: PhantomInvariantLifetime<'f>,
71+
overflow_arg_area: *const c_void,
72+
reg_save_area: *const c_void,
7773
}
7874
}
7975
target_arch = "xtensa" => {
8076
/// Xtensa ABI implementation of a `va_list`.
8177
#[repr(C)]
8278
#[derive(Debug)]
83-
#[lang = "va_list"]
84-
pub struct VaListImpl<'f> {
85-
stk: *mut i32,
86-
reg: *mut i32,
79+
#[rustc_pass_indirectly_in_non_rustic_abis]
80+
struct VaListInner {
81+
stk: *const i32,
82+
reg: *const i32,
8783
ndx: i32,
88-
_marker: PhantomInvariantLifetime<'f>,
8984
}
9085
}
9186

@@ -94,94 +89,32 @@ crate::cfg_select! {
9489
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
9590
// - windows
9691
// - uefi
97-
// - any other target for which we don't specify the `VaListImpl` above
92+
// - any other target for which we don't specify the `VaListInner` above
9893
//
9994
// In this implementation the `va_list` type is just an alias for an opaque pointer.
10095
// That pointer is probably just the next variadic argument on the caller's stack.
10196
_ => {
10297
/// Basic implementation of a `va_list`.
10398
#[repr(transparent)]
104-
#[lang = "va_list"]
105-
pub struct VaListImpl<'f> {
106-
ptr: *mut c_void,
107-
108-
// Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
109-
// the region of the function it's defined in
110-
_marker: PhantomInvariantLifetime<'f>,
111-
}
112-
113-
impl<'f> fmt::Debug for VaListImpl<'f> {
114-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115-
write!(f, "va_list* {:p}", self.ptr)
116-
}
117-
}
118-
}
119-
}
120-
121-
crate::cfg_select! {
122-
all(
123-
any(
124-
target_arch = "aarch64",
125-
target_arch = "powerpc",
126-
target_arch = "s390x",
127-
target_arch = "x86_64"
128-
),
129-
not(target_arch = "xtensa"),
130-
any(not(target_arch = "aarch64"), not(target_vendor = "apple")),
131-
not(target_family = "wasm"),
132-
not(target_os = "uefi"),
133-
not(windows),
134-
) => {
135-
/// A wrapper for a `va_list`
136-
#[repr(transparent)]
137-
#[derive(Debug)]
138-
pub struct VaList<'a, 'f: 'a> {
139-
inner: &'a mut VaListImpl<'f>,
140-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
141-
}
142-
143-
144-
impl<'f> VaListImpl<'f> {
145-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
146-
#[inline]
147-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
148-
VaList { inner: self, _marker: PhantomData }
149-
}
150-
}
151-
}
152-
153-
_ => {
154-
/// A wrapper for a `va_list`
155-
#[repr(transparent)]
15699
#[derive(Debug)]
157-
pub struct VaList<'a, 'f: 'a> {
158-
inner: VaListImpl<'f>,
159-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
160-
}
161-
162-
impl<'f> VaListImpl<'f> {
163-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
164-
#[inline]
165-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
166-
VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
167-
}
100+
struct VaListInner {
101+
ptr: *const c_void,
168102
}
169103
}
170104
}
171105

172-
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
173-
type Target = VaListImpl<'f>;
174-
175-
#[inline]
176-
fn deref(&self) -> &VaListImpl<'f> {
177-
&self.inner
178-
}
106+
/// A variable argument list, equivalent to `va_list` in C.
107+
#[repr(transparent)]
108+
#[lang = "va_list"]
109+
pub struct VaList<'a> {
110+
inner: VaListInner,
111+
_marker: PhantomCovariantLifetime<'a>,
179112
}
180113

181-
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
182-
#[inline]
183-
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
184-
&mut self.inner
114+
impl fmt::Debug for VaList<'_> {
115+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116+
// No need to include `_marker` in debug output.
117+
f.debug_tuple("VaList").field(&self.inner).finish()
185118
}
186119
}
187120

@@ -202,7 +135,7 @@ mod sealed {
202135
impl<T> Sealed for *const T {}
203136
}
204137

205-
/// Trait which permits the allowed types to be used with [`VaListImpl::arg`].
138+
/// Trait which permits the allowed types to be used with [`VaList::arg`].
206139
///
207140
/// # Safety
208141
///
@@ -232,52 +165,31 @@ unsafe impl VaArgSafe for f64 {}
232165
unsafe impl<T> VaArgSafe for *mut T {}
233166
unsafe impl<T> VaArgSafe for *const T {}
234167

235-
impl<'f> VaListImpl<'f> {
168+
impl<'f> VaList<'f> {
236169
/// Advance to the next arg.
237170
#[inline]
238171
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
239172
// SAFETY: the caller must uphold the safety contract for `va_arg`.
240173
unsafe { va_arg(self) }
241174
}
242-
243-
/// Copies the `va_list` at the current location.
244-
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
245-
where
246-
F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
247-
{
248-
let mut ap = self.clone();
249-
let ret = f(ap.as_va_list());
250-
// SAFETY: the caller must uphold the safety contract for `va_end`.
251-
unsafe {
252-
va_end(&mut ap);
253-
}
254-
ret
255-
}
256175
}
257176

258-
impl<'f> Clone for VaListImpl<'f> {
177+
impl<'f> Clone for VaList<'f> {
259178
#[inline]
260179
fn clone(&self) -> Self {
261180
let mut dest = crate::mem::MaybeUninit::uninit();
262-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
181+
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
263182
unsafe {
264183
va_copy(dest.as_mut_ptr(), self);
265184
dest.assume_init()
266185
}
267186
}
268187
}
269188

270-
impl<'f> Drop for VaListImpl<'f> {
189+
impl<'f> Drop for VaList<'f> {
271190
fn drop(&mut self) {
272-
// FIXME: this should call `va_end`, but there's no clean way to
273-
// guarantee that `drop` always gets inlined into its caller,
274-
// so the `va_end` would get directly called from the same function as
275-
// the corresponding `va_copy`. `man va_end` states that C requires this,
276-
// and LLVM basically follows the C semantics, so we need to make sure
277-
// that `va_end` is always called from the same function as `va_copy`.
278-
// For more details, see https://github.com/rust-lang/rust/pull/59625
279-
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
280-
//
281-
// This works for now, since `va_end` is a no-op on all current LLVM targets.
191+
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
192+
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
193+
// destructor is empty.
282194
}
283195
}

library/core/src/intrinsics/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
)]
5555
#![allow(missing_docs)]
5656

57-
use crate::ffi::va_list::{VaArgSafe, VaListImpl};
57+
use crate::ffi::va_list::{VaArgSafe, VaList};
5858
use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple};
5959
use crate::ptr;
6060

@@ -3192,19 +3192,19 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
31923192
/// FIXME: document safety requirements
31933193
#[rustc_intrinsic]
31943194
#[rustc_nounwind]
3195-
pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
3195+
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
31963196

31973197
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
31983198
/// argument `ap` points to.
31993199
///
32003200
/// FIXME: document safety requirements
32013201
#[rustc_intrinsic]
32023202
#[rustc_nounwind]
3203-
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
3203+
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
32043204

32053205
/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`.
32063206
///
32073207
/// FIXME: document safety requirements
32083208
#[rustc_intrinsic]
32093209
#[rustc_nounwind]
3210-
pub unsafe fn va_end(ap: &mut VaListImpl<'_>);
3210+
pub unsafe fn va_end(ap: &mut VaList<'_>);

library/std/src/ffi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub use core::ffi::c_void;
172172
all supported platforms",
173173
issue = "44930"
174174
)]
175-
pub use core::ffi::{VaArgSafe, VaList, VaListImpl};
175+
pub use core::ffi::{VaArgSafe, VaList};
176176
#[stable(feature = "core_ffi_c", since = "1.64.0")]
177177
pub use core::ffi::{
178178
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,

src/tools/compiletest/src/directives.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
879879
"ignore-thumbv8m.base-none-eabi",
880880
"ignore-thumbv8m.main-none-eabi",
881881
"ignore-tvos",
882+
"ignore-uefi",
882883
"ignore-unix",
883884
"ignore-unknown",
884885
"ignore-uwp",

tests/auxiliary/rust_test_helpers.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ double rust_interesting_average(uint64_t n, ...) {
314314
return sum;
315315
}
316316

317+
int32_t rust_va_list_next_i32(va_list* ap) {
318+
return va_arg(*ap, int32_t);
319+
}
320+
317321
int32_t rust_int8_to_int32(int8_t x) {
318322
return (int32_t)x;
319323
}

tests/codegen-llvm/cffi/c-variadic-copy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Tests that `VaListImpl::clone` gets inlined into a call to `llvm.va_copy`
1+
// Tests that `VaList::clone` gets inlined into a call to `llvm.va_copy`
22

33
#![crate_type = "lib"]
44
#![feature(c_variadic)]
@@ -12,5 +12,5 @@ extern "C" {
1212
pub unsafe extern "C" fn clone_variadic(ap: VaList) {
1313
let mut ap2 = ap.clone();
1414
// CHECK: call void @llvm.va_copy
15-
foreign_c_variadic_1(ap2.as_va_list(), 42i32);
15+
foreign_c_variadic_1(ap2, 42i32);
1616
}

0 commit comments

Comments
 (0)