Skip to content

Commit 5490a8c

Browse files
beetreesfolkertdev
authored andcommitted
Rework c_variadic
1 parent 764e069 commit 5490a8c

File tree

22 files changed

+586
-286
lines changed

22 files changed

+586
-286
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: 47 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
55
#[cfg(not(target_arch = "xtensa"))]
66
use crate::ffi::c_void;
7-
#[allow(unused_imports)]
87
use crate::fmt;
9-
use crate::intrinsics::{va_arg, va_copy, va_end};
10-
use crate::marker::{PhantomData, PhantomInvariantLifetime};
11-
use crate::ops::{Deref, DerefMut};
8+
use crate::intrinsics::{va_arg, va_copy};
9+
use crate::marker::PhantomCovariantLifetime;
1210

13-
// The name is WIP, using `VaListImpl` for now.
14-
//
1511
// Most targets explicitly specify the layout of `va_list`, this layout is matched here.
12+
// For `va_list`s which are single-element array in C (and therefore experience array-to-pointer
13+
// decay when passed as arguments in C), the `VaList` struct is annotated with
14+
// `#[rustc_pass_indirectly_in_non_rustic_abis]`. This ensures that the compiler uses the correct
15+
// ABI for functions like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly.
1616
crate::cfg_select! {
1717
all(
1818
target_arch = "aarch64",
@@ -27,66 +27,60 @@ crate::cfg_select! {
2727
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
2828
#[repr(C)]
2929
#[derive(Debug)]
30-
#[lang = "va_list"]
31-
pub struct VaListImpl<'f> {
32-
stack: *mut c_void,
33-
gr_top: *mut c_void,
34-
vr_top: *mut c_void,
30+
struct VaListInner {
31+
stack: *const c_void,
32+
gr_top: *const c_void,
33+
vr_top: *const c_void,
3534
gr_offs: i32,
3635
vr_offs: i32,
37-
_marker: PhantomInvariantLifetime<'f>,
3836
}
3937
}
4038
all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => {
4139
/// PowerPC ABI implementation of a `va_list`.
4240
#[repr(C)]
4341
#[derive(Debug)]
44-
#[lang = "va_list"]
45-
pub struct VaListImpl<'f> {
42+
#[rustc_pass_indirectly_in_non_rustic_abis]
43+
struct VaListInner {
4644
gpr: u8,
4745
fpr: u8,
4846
reserved: u16,
49-
overflow_arg_area: *mut c_void,
50-
reg_save_area: *mut c_void,
51-
_marker: PhantomInvariantLifetime<'f>,
47+
overflow_arg_area: *const c_void,
48+
reg_save_area: *const c_void,
5249
}
5350
}
5451
target_arch = "s390x" => {
5552
/// s390x ABI implementation of a `va_list`.
5653
#[repr(C)]
5754
#[derive(Debug)]
58-
#[lang = "va_list"]
59-
pub struct VaListImpl<'f> {
55+
#[rustc_pass_indirectly_in_non_rustic_abis]
56+
struct VaListInner {
6057
gpr: i64,
6158
fpr: i64,
62-
overflow_arg_area: *mut c_void,
63-
reg_save_area: *mut c_void,
64-
_marker: PhantomInvariantLifetime<'f>,
59+
overflow_arg_area: *const c_void,
60+
reg_save_area: *const c_void,
6561
}
6662
}
6763
all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => {
6864
/// x86_64 ABI implementation of a `va_list`.
6965
#[repr(C)]
7066
#[derive(Debug)]
71-
#[lang = "va_list"]
72-
pub struct VaListImpl<'f> {
67+
#[rustc_pass_indirectly_in_non_rustic_abis]
68+
struct VaListInner {
7369
gp_offset: i32,
7470
fp_offset: i32,
75-
overflow_arg_area: *mut c_void,
76-
reg_save_area: *mut c_void,
77-
_marker: PhantomInvariantLifetime<'f>,
71+
overflow_arg_area: *const c_void,
72+
reg_save_area: *const c_void,
7873
}
7974
}
8075
target_arch = "xtensa" => {
8176
/// Xtensa ABI implementation of a `va_list`.
8277
#[repr(C)]
8378
#[derive(Debug)]
84-
#[lang = "va_list"]
85-
pub struct VaListImpl<'f> {
86-
stk: *mut i32,
87-
reg: *mut i32,
79+
#[rustc_pass_indirectly_in_non_rustic_abis]
80+
struct VaListInner {
81+
stk: *const i32,
82+
reg: *const i32,
8883
ndx: i32,
89-
_marker: PhantomInvariantLifetime<'f>,
9084
}
9185
}
9286

@@ -95,94 +89,32 @@ crate::cfg_select! {
9589
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
9690
// - windows
9791
// - uefi
98-
// - 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
9993
//
10094
// In this implementation the `va_list` type is just an alias for an opaque pointer.
10195
// That pointer is probably just the next variadic argument on the caller's stack.
10296
_ => {
10397
/// Basic implementation of a `va_list`.
10498
#[repr(transparent)]
105-
#[lang = "va_list"]
106-
pub struct VaListImpl<'f> {
107-
ptr: *mut c_void,
108-
109-
// Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
110-
// the region of the function it's defined in
111-
_marker: PhantomInvariantLifetime<'f>,
112-
}
113-
114-
impl<'f> fmt::Debug for VaListImpl<'f> {
115-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116-
write!(f, "va_list* {:p}", self.ptr)
117-
}
118-
}
119-
}
120-
}
121-
122-
crate::cfg_select! {
123-
all(
124-
any(
125-
target_arch = "aarch64",
126-
target_arch = "powerpc",
127-
target_arch = "s390x",
128-
target_arch = "x86_64"
129-
),
130-
not(target_arch = "xtensa"),
131-
any(not(target_arch = "aarch64"), not(target_vendor = "apple")),
132-
not(target_family = "wasm"),
133-
not(target_os = "uefi"),
134-
not(windows),
135-
) => {
136-
/// A wrapper for a `va_list`
137-
#[repr(transparent)]
138-
#[derive(Debug)]
139-
pub struct VaList<'a, 'f: 'a> {
140-
inner: &'a mut VaListImpl<'f>,
141-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
142-
}
143-
144-
145-
impl<'f> VaListImpl<'f> {
146-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
147-
#[inline]
148-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
149-
VaList { inner: self, _marker: PhantomData }
150-
}
151-
}
152-
}
153-
154-
_ => {
155-
/// A wrapper for a `va_list`
156-
#[repr(transparent)]
15799
#[derive(Debug)]
158-
pub struct VaList<'a, 'f: 'a> {
159-
inner: VaListImpl<'f>,
160-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
161-
}
162-
163-
impl<'f> VaListImpl<'f> {
164-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
165-
#[inline]
166-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
167-
VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
168-
}
100+
struct VaListInner {
101+
ptr: *const c_void,
169102
}
170103
}
171104
}
172105

173-
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
174-
type Target = VaListImpl<'f>;
175-
176-
#[inline]
177-
fn deref(&self) -> &VaListImpl<'f> {
178-
&self.inner
179-
}
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>,
180112
}
181113

182-
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
183-
#[inline]
184-
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
185-
&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()
186118
}
187119
}
188120

@@ -203,7 +135,7 @@ mod sealed {
203135
impl<T> Sealed for *const T {}
204136
}
205137

206-
/// Types that are valid to read using [`VaListImpl::arg`].
138+
/// Types that are valid to read using [`VaList::arg`].
207139
///
208140
/// # Safety
209141
///
@@ -238,7 +170,7 @@ unsafe impl VaArgSafe for f64 {}
238170
unsafe impl<T> VaArgSafe for *mut T {}
239171
unsafe impl<T> VaArgSafe for *const T {}
240172

241-
impl<'f> VaListImpl<'f> {
173+
impl<'f> VaList<'f> {
242174
/// Advance to and read the next variable argument.
243175
///
244176
/// # Safety
@@ -257,46 +189,25 @@ impl<'f> VaListImpl<'f> {
257189
// SAFETY: the caller must uphold the safety contract for `va_arg`.
258190
unsafe { va_arg(self) }
259191
}
260-
261-
/// Copies the `va_list` at the current location.
262-
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
263-
where
264-
F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
265-
{
266-
let mut ap = self.clone();
267-
let ret = f(ap.as_va_list());
268-
// SAFETY: the caller must uphold the safety contract for `va_end`.
269-
unsafe {
270-
va_end(&mut ap);
271-
}
272-
ret
273-
}
274192
}
275193

276-
impl<'f> Clone for VaListImpl<'f> {
194+
impl<'f> Clone for VaList<'f> {
277195
#[inline]
278196
fn clone(&self) -> Self {
279197
let mut dest = crate::mem::MaybeUninit::uninit();
280-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
198+
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
281199
unsafe {
282200
va_copy(dest.as_mut_ptr(), self);
283201
dest.assume_init()
284202
}
285203
}
286204
}
287205

288-
impl<'f> Drop for VaListImpl<'f> {
206+
impl<'f> Drop for VaList<'f> {
289207
fn drop(&mut self) {
290-
// FIXME: this should call `va_end`, but there's no clean way to
291-
// guarantee that `drop` always gets inlined into its caller,
292-
// so the `va_end` would get directly called from the same function as
293-
// the corresponding `va_copy`. `man va_end` states that C requires this,
294-
// and LLVM basically follows the C semantics, so we need to make sure
295-
// that `va_end` is always called from the same function as `va_copy`.
296-
// For more details, see https://github.com/rust-lang/rust/pull/59625
297-
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
298-
//
299-
// This works for now, since `va_end` is a no-op on all current LLVM targets.
208+
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
209+
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
210+
// destructor is empty.
300211
}
301212
}
302213

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

@@ -3301,19 +3301,19 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
33013301
/// FIXME: document safety requirements
33023302
#[rustc_intrinsic]
33033303
#[rustc_nounwind]
3304-
pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
3304+
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
33053305

33063306
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
33073307
/// argument `ap` points to.
33083308
///
33093309
/// FIXME: document safety requirements
33103310
#[rustc_intrinsic]
33113311
#[rustc_nounwind]
3312-
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
3312+
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;
33133313

33143314
/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`.
33153315
///
33163316
/// FIXME: document safety requirements
33173317
#[rustc_intrinsic]
33183318
#[rustc_nounwind]
3319-
pub unsafe fn va_end(ap: &mut VaListImpl<'_>);
3319+
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/directive_names.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
112112
"ignore-thumbv8m.base-none-eabi",
113113
"ignore-thumbv8m.main-none-eabi",
114114
"ignore-tvos",
115+
"ignore-uefi",
115116
"ignore-unix",
116117
"ignore-unknown",
117118
"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)