Skip to content

Commit 2bb2fc9

Browse files
committed
Rework c_variadic
1 parent a281345 commit 2bb2fc9

File tree

24 files changed

+649
-236
lines changed

24 files changed

+649
-236
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
14501450
}
14511451

14521452
// FIXME implement variadics in cranelift
1453-
sym::va_copy | sym::va_arg | sym::va_end => {
1453+
sym::va_copy | sym::va_arg => {
14541454
fx.tcx.dcx().span_fatal(
14551455
source_info.span,
14561456
"Defining variadic functions is not yet supported by Cranelift",

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
@@ -2310,9 +2310,7 @@ symbols! {
23102310
v8plus,
23112311
va_arg,
23122312
va_copy,
2313-
va_end,
23142313
va_list,
2315-
va_start,
23162314
val,
23172315
validity,
23182316
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: 42 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,27 @@
55
use crate::ffi::c_void;
66
#[allow(unused_imports)]
77
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};
8+
use crate::intrinsics::{va_arg, va_copy};
9+
use crate::marker::PhantomInvariantLifetime;
1110

12-
// The name is WIP, using `VaListImpl` for now.
13-
//
1411
// 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.
16+
17+
// Note that currently support for `#[rustc_pass_indirectly_in_non_rustic_abis]` is only implemented
18+
// on architectures which need it here, so when adding support for a new architecture the following
19+
// will need to happen:
20+
//
21+
// - Check that the calling conventions used on the new architecture correctly check
22+
// `arg.layout.pass_indirectly_in_non_rustic_abis()` and call `arg.make_indirect()` if it returns
23+
// `true`.
24+
// - Add a revision to the `tests/ui/abi/pass-indirectly-attr.rs` test for the new architecture.
25+
// - Add the new architecture to the `supported_architectures` array in the
26+
// `check_pass_indirectly_in_non_rustic_abis` function in
27+
// `compiler/rustc_passes/src/check_attr.rs`. This will stop the compiler from emitting an error
28+
// message when the attribute is used on that architecture.
1529
crate::cfg_select! {
1630
all(
1731
target_arch = "aarch64",
@@ -27,7 +41,8 @@ crate::cfg_select! {
2741
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
2842
#[derive(Debug)]
2943
#[lang = "va_list"]
30-
pub struct VaListImpl<'f> {
44+
#[rustc_pass_indirectly_in_non_rustic_abis]
45+
pub struct VaList<'f> {
3146
stack: *mut c_void,
3247
gr_top: *mut c_void,
3348
vr_top: *mut c_void,
@@ -41,7 +56,8 @@ crate::cfg_select! {
4156
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
4257
#[derive(Debug)]
4358
#[lang = "va_list"]
44-
pub struct VaListImpl<'f> {
59+
#[rustc_pass_indirectly_in_non_rustic_abis]
60+
pub struct VaList<'f> {
4561
gpr: u8,
4662
fpr: u8,
4763
reserved: u16,
@@ -55,7 +71,8 @@ crate::cfg_select! {
5571
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
5672
#[derive(Debug)]
5773
#[lang = "va_list"]
58-
pub struct VaListImpl<'f> {
74+
#[rustc_pass_indirectly_in_non_rustic_abis]
75+
pub struct VaList<'f> {
5976
gpr: i64,
6077
fpr: i64,
6178
overflow_arg_area: *mut c_void,
@@ -68,7 +85,8 @@ crate::cfg_select! {
6885
#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
6986
#[derive(Debug)]
7087
#[lang = "va_list"]
71-
pub struct VaListImpl<'f> {
88+
#[rustc_pass_indirectly_in_non_rustic_abis]
89+
pub struct VaList<'f> {
7290
gp_offset: i32,
7391
fp_offset: i32,
7492
overflow_arg_area: *mut c_void,
@@ -81,7 +99,8 @@ crate::cfg_select! {
8199
#[repr(C)]
82100
#[derive(Debug)]
83101
#[lang = "va_list"]
84-
pub struct VaListImpl<'f> {
102+
#[rustc_pass_indirectly_in_non_rustic_abis]
103+
pub struct VaList<'f> {
85104
stk: *mut i32,
86105
reg: *mut i32,
87106
ndx: i32,
@@ -94,97 +113,30 @@ crate::cfg_select! {
94113
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
95114
// - windows
96115
// - uefi
97-
// - any other target for which we don't specify the `VaListImpl` above
116+
// - any other target for which we don't specify the `VaList` above
98117
//
99118
// In this implementation the `va_list` type is just an alias for an opaque pointer.
100119
// That pointer is probably just the next variadic argument on the caller's stack.
101120
_ => {
102121
/// Basic implementation of a `va_list`.
103122
#[repr(transparent)]
104123
#[lang = "va_list"]
105-
pub struct VaListImpl<'f> {
124+
pub struct VaList<'f> {
106125
ptr: *mut c_void,
107126

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

113-
impl<'f> fmt::Debug for VaListImpl<'f> {
132+
impl<'f> fmt::Debug for VaList<'f> {
114133
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115-
write!(f, "va_list* {:p}", self.ptr)
134+
f.debug_tuple("VaList").field(&self.ptr).finish()
116135
}
117136
}
118137
}
119138
}
120139

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)]
156-
#[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-
}
168-
}
169-
}
170-
}
171-
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-
}
179-
}
180-
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
185-
}
186-
}
187-
188140
mod sealed {
189141
pub trait Sealed {}
190142

@@ -202,7 +154,7 @@ mod sealed {
202154
impl<T> Sealed for *const T {}
203155
}
204156

205-
/// Trait which permits the allowed types to be used with [`VaListImpl::arg`].
157+
/// Trait which permits the allowed types to be used with [`VaList::arg`].
206158
///
207159
/// # Safety
208160
///
@@ -232,52 +184,31 @@ unsafe impl VaArgSafe for f64 {}
232184
unsafe impl<T> VaArgSafe for *mut T {}
233185
unsafe impl<T> VaArgSafe for *const T {}
234186

235-
impl<'f> VaListImpl<'f> {
187+
impl<'f> VaList<'f> {
236188
/// Advance to the next arg.
237189
#[inline]
238190
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
239191
// SAFETY: the caller must uphold the safety contract for `va_arg`.
240192
unsafe { va_arg(self) }
241193
}
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-
}
256194
}
257195

258-
impl<'f> Clone for VaListImpl<'f> {
196+
impl<'f> Clone for VaList<'f> {
259197
#[inline]
260198
fn clone(&self) -> Self {
261199
let mut dest = crate::mem::MaybeUninit::uninit();
262-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
200+
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
263201
unsafe {
264202
va_copy(dest.as_mut_ptr(), self);
265203
dest.assume_init()
266204
}
267205
}
268206
}
269207

270-
impl<'f> Drop for VaListImpl<'f> {
208+
impl<'f> Drop for VaList<'f> {
271209
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.
210+
// Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
211+
// (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
212+
// destructor is empty.
282213
}
283214
}

library/core/src/intrinsics/mod.rs

Lines changed: 3 additions & 10 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

@@ -3149,19 +3149,12 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
31493149
/// FIXME: document safety requirements
31503150
#[rustc_intrinsic]
31513151
#[rustc_nounwind]
3152-
pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
3152+
pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>);
31533153

31543154
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
31553155
/// argument `ap` points to.
31563156
///
31573157
/// FIXME: document safety requirements
31583158
#[rustc_intrinsic]
31593159
#[rustc_nounwind]
3160-
pub unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
3161-
3162-
/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`.
3163-
///
3164-
/// FIXME: document safety requirements
3165-
#[rustc_intrinsic]
3166-
#[rustc_nounwind]
3167-
pub unsafe fn va_end(ap: &mut VaListImpl<'_>);
3160+
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)