Skip to content

Commit 6347a21

Browse files
committed
Rework c_variadic
1 parent 2a196cd commit 6347a21

File tree

23 files changed

+651
-239
lines changed

23 files changed

+651
-239
lines changed

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
131131
return Ok(());
132132
}
133133

134-
sym::va_start => bx.va_start(args[0].immediate()),
135-
sym::va_end => bx.va_end(args[0].immediate()),
136134
sym::size_of_val => {
137135
let tp_ty = fn_args.type_at(0);
138136
let (_, meta) = args[0].val.pointer_parts();

compiler/rustc_codegen_ssa/src/traits/intrinsic.rs

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

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -506,10 +506,6 @@ pub(crate) fn check_intrinsic_type(
506506
)
507507
}
508508

509-
sym::va_start | sym::va_end => {
510-
(0, 0, vec![mk_va_list_ty(hir::Mutability::Mut).0], tcx.types.unit)
511-
}
512-
513509
sym::va_copy => {
514510
let (va_list_ref_ty, va_list_ty) = mk_va_list_ty(hir::Mutability::Not);
515511
let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);

compiler/rustc_span/src/symbol.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,9 +2311,7 @@ symbols! {
23112311
v8plus,
23122312
va_arg,
23132313
va_copy,
2314-
va_end,
23152314
va_list,
2316-
va_start,
23172315
val,
23182316
validity,
23192317
values,

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: 43 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,26 @@
55
use crate::ffi::c_void;
66
#[allow(unused_imports)]
77
use crate::fmt;
8-
use crate::marker::{PhantomData, PhantomInvariantLifetime};
9-
use crate::ops::{Deref, DerefMut};
8+
use crate::marker::PhantomInvariantLifetime;
109

11-
// The name is WIP, using `VaListImpl` for now.
12-
//
1310
// 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.
15+
16+
// Note that currently support for `#[rustc_pass_indirectly_in_non_rustic_abis]` is only implemented
17+
// on architectures which need it here, so when adding support for a new architecture the following
18+
// will need to happen:
19+
//
20+
// - Check that the calling conventions used on the new architecture correctly check
21+
// `arg.layout.pass_indirectly_in_non_rustic_abis()` and call `arg.make_indirect()` if it returns
22+
// `true`.
23+
// - Add a revision to the `tests/ui/abi/pass-indirectly-attr.rs` test for the new architecture.
24+
// - Add the new architecture to the `supported_architectures` array in the
25+
// `check_pass_indirectly_in_non_rustic_abis` function in
26+
// `compiler/rustc_passes/src/check_attr.rs`. This will stop the compiler from emitting an error
27+
// message when the attribute is used on that architecture.
1428
crate::cfg_select! {
1529
all(
1630
target_arch = "aarch64",
@@ -26,7 +40,8 @@ crate::cfg_select! {
2640
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
2741
#[derive(Debug)]
2842
#[lang = "va_list"]
29-
pub struct VaListImpl<'f> {
43+
#[rustc_pass_indirectly_in_non_rustic_abis]
44+
pub struct VaList<'f> {
3045
stack: *mut c_void,
3146
gr_top: *mut c_void,
3247
vr_top: *mut c_void,
@@ -40,7 +55,8 @@ crate::cfg_select! {
4055
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
4156
#[derive(Debug)]
4257
#[lang = "va_list"]
43-
pub struct VaListImpl<'f> {
58+
#[rustc_pass_indirectly_in_non_rustic_abis]
59+
pub struct VaList<'f> {
4460
gpr: u8,
4561
fpr: u8,
4662
reserved: u16,
@@ -54,7 +70,8 @@ crate::cfg_select! {
5470
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
5571
#[derive(Debug)]
5672
#[lang = "va_list"]
57-
pub struct VaListImpl<'f> {
73+
#[rustc_pass_indirectly_in_non_rustic_abis]
74+
pub struct VaList<'f> {
5875
gpr: i64,
5976
fpr: i64,
6077
overflow_arg_area: *mut c_void,
@@ -67,7 +84,8 @@ crate::cfg_select! {
6784
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
6885
#[derive(Debug)]
6986
#[lang = "va_list"]
70-
pub struct VaListImpl<'f> {
87+
#[rustc_pass_indirectly_in_non_rustic_abis]
88+
pub struct VaList<'f> {
7189
gp_offset: i32,
7290
fp_offset: i32,
7391
overflow_arg_area: *mut c_void,
@@ -80,7 +98,8 @@ crate::cfg_select! {
8098
#[repr(C)]
8199
#[derive(Debug)]
82100
#[lang = "va_list"]
83-
pub struct VaListImpl<'f> {
101+
#[rustc_pass_indirectly_in_non_rustic_abis]
102+
pub struct VaList<'f> {
84103
stk: *mut i32,
85104
reg: *mut i32,
86105
ndx: i32,
@@ -93,97 +112,30 @@ crate::cfg_select! {
93112
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
94113
// - windows
95114
// - uefi
96-
// - any other target for which we don't specify the `VaListImpl` above
115+
// - any other target for which we don't specify the `VaList` above
97116
//
98117
// In this implementation the `va_list` type is just an alias for an opaque pointer.
99118
// That pointer is probably just the next variadic argument on the caller's stack.
100119
_ => {
101120
/// Basic implementation of a `va_list`.
102121
#[repr(transparent)]
103122
#[lang = "va_list"]
104-
pub struct VaListImpl<'f> {
123+
pub struct VaList<'f> {
105124
ptr: *mut c_void,
106125

107-
// Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
126+
// Invariant over `'f`, so each `VaList<'f>` object is tied to
108127
// the region of the function it's defined in
109128
_marker: PhantomInvariantLifetime<'f>,
110129
}
111130

112-
impl<'f> fmt::Debug for VaListImpl<'f> {
131+
impl<'f> fmt::Debug for VaList<'f> {
113132
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114-
write!(f, "va_list* {:p}", self.ptr)
115-
}
116-
}
117-
}
118-
}
119-
120-
crate::cfg_select! {
121-
all(
122-
any(
123-
target_arch = "aarch64",
124-
target_arch = "powerpc",
125-
target_arch = "s390x",
126-
target_arch = "x86_64"
127-
),
128-
not(target_arch = "xtensa"),
129-
any(not(target_arch = "aarch64"), not(target_vendor = "apple")),
130-
not(target_family = "wasm"),
131-
not(target_os = "uefi"),
132-
not(windows),
133-
) => {
134-
/// A wrapper for a `va_list`
135-
#[repr(transparent)]
136-
#[derive(Debug)]
137-
pub struct VaList<'a, 'f: 'a> {
138-
inner: &'a mut VaListImpl<'f>,
139-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
140-
}
141-
142-
143-
impl<'f> VaListImpl<'f> {
144-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
145-
#[inline]
146-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
147-
VaList { inner: self, _marker: PhantomData }
148-
}
149-
}
150-
}
151-
152-
_ => {
153-
/// A wrapper for a `va_list`
154-
#[repr(transparent)]
155-
#[derive(Debug)]
156-
pub struct VaList<'a, 'f: 'a> {
157-
inner: VaListImpl<'f>,
158-
_marker: PhantomData<&'a mut VaListImpl<'f>>,
159-
}
160-
161-
impl<'f> VaListImpl<'f> {
162-
/// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
163-
#[inline]
164-
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
165-
VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
133+
f.debug_tuple("VaList").field(&self.ptr).finish()
166134
}
167135
}
168136
}
169137
}
170138

171-
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
172-
type Target = VaListImpl<'f>;
173-
174-
#[inline]
175-
fn deref(&self) -> &VaListImpl<'f> {
176-
&self.inner
177-
}
178-
}
179-
180-
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
181-
#[inline]
182-
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
183-
&mut self.inner
184-
}
185-
}
186-
187139
mod sealed {
188140
pub trait Sealed {}
189141

@@ -201,7 +153,7 @@ mod sealed {
201153
impl<T> Sealed for *const T {}
202154
}
203155

204-
/// Trait which permits the allowed types to be used with [`VaListImpl::arg`].
156+
/// Trait which permits the allowed types to be used with [`VaList::arg`].
205157
///
206158
/// # Safety
207159
///
@@ -231,69 +183,31 @@ unsafe impl VaArgSafe for f64 {}
231183
unsafe impl<T> VaArgSafe for *mut T {}
232184
unsafe impl<T> VaArgSafe for *const T {}
233185

234-
impl<'f> VaListImpl<'f> {
186+
impl<'f> VaList<'f> {
235187
/// Advance to the next arg.
236188
#[inline]
237189
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
238190
// SAFETY: the caller must uphold the safety contract for `va_arg`.
239-
unsafe { va_arg(self) }
240-
}
241-
242-
/// Copies the `va_list` at the current location.
243-
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
244-
where
245-
F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
246-
{
247-
let mut ap = self.clone();
248-
let ret = f(ap.as_va_list());
249-
// SAFETY: the caller must uphold the safety contract for `va_end`.
250-
unsafe {
251-
va_end(&mut ap);
252-
}
253-
ret
191+
unsafe { crate::intrinsics::va_arg(self) }
254192
}
255193
}
256194

257-
impl<'f> Clone for VaListImpl<'f> {
195+
impl<'f> Clone for VaList<'f> {
258196
#[inline]
259197
fn clone(&self) -> Self {
260198
let mut dest = crate::mem::MaybeUninit::uninit();
261-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
199+
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
262200
unsafe {
263-
va_copy(dest.as_mut_ptr(), self);
201+
crate::intrinsics::va_copy(dest.as_mut_ptr(), self);
264202
dest.assume_init()
265203
}
266204
}
267205
}
268206

269-
impl<'f> Drop for VaListImpl<'f> {
207+
impl<'f> Drop for VaList<'f> {
270208
fn drop(&mut self) {
271-
// FIXME: this should call `va_end`, but there's no clean way to
272-
// guarantee that `drop` always gets inlined into its caller,
273-
// so the `va_end` would get directly called from the same function as
274-
// the corresponding `va_copy`. `man va_end` states that C requires this,
275-
// and LLVM basically follows the C semantics, so we need to make sure
276-
// that `va_end` is always called from the same function as `va_copy`.
277-
// For more details, see https://github.com/rust-lang/rust/pull/59625
278-
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
279-
//
280-
// This works for now, since `va_end` is a no-op on all current LLVM targets.
209+
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
210+
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
211+
// destructor is empty.
281212
}
282213
}
283-
284-
/// Destroy the arglist `ap` after initialization with `va_start` or
285-
/// `va_copy`.
286-
#[rustc_intrinsic]
287-
#[rustc_nounwind]
288-
unsafe fn va_end(ap: &mut VaListImpl<'_>);
289-
290-
/// Copies the current location of arglist `src` to the arglist `dst`.
291-
#[rustc_intrinsic]
292-
#[rustc_nounwind]
293-
unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
294-
295-
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
296-
/// argument `ap` points to.
297-
#[rustc_intrinsic]
298-
#[rustc_nounwind]
299-
unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;

library/core/src/intrinsics/mod.rs

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

57+
use crate::ffi::{VaArgSafe, VaList};
5758
use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple};
5859
use crate::ptr;
5960

@@ -3142,3 +3143,14 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
31423143
}
31433144
)
31443145
}
3146+
3147+
/// Copies the current location of arglist `src` to the arglist `dst`.
3148+
#[rustc_intrinsic]
3149+
#[rustc_nounwind]
3150+
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
3151+
3152+
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
3153+
/// argument `ap` points to.
3154+
#[rustc_intrinsic]
3155+
#[rustc_nounwind]
3156+
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaList<'_>) -> T;

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,

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/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)