Skip to content

Commit c923e31

Browse files
committed
Auto merge of #149552 - matthiaskrgr:rollup-j8hm2a1, r=matthiaskrgr
Rollup of 4 pull requests Successful merges: - #141980 (Rework `c_variadic`) - #146436 (Slice iter cleanup) - #148250 (array_chunks: slightly improve docs) - #149520 (also introduce Peekable::next_if_map_mut next to next_if_map) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 646a3f8 + 7158f11 commit c923e31

File tree

27 files changed

+812
-466
lines changed

27 files changed

+812
-466
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/array/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ const fn try_from_fn_erased<R: [const] Try<Output: [const] Destruct>>(
924924
/// All write accesses to this structure are unsafe and must maintain a correct
925925
/// count of `initialized` elements.
926926
///
927-
/// To minimize indirection fields are still pub but callers should at least use
927+
/// To minimize indirection, fields are still pub but callers should at least use
928928
/// `push_unchecked` to signal that something unsafe is going on.
929929
struct Guard<'a, T> {
930930
/// The array to be initialized.
@@ -943,7 +943,7 @@ impl<T> Guard<'_, T> {
943943
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
944944
pub(crate) const unsafe fn push_unchecked(&mut self, item: T) {
945945
// SAFETY: If `initialized` was correct before and the caller does not
946-
// invoke this method more than N times then writes will be in-bounds
946+
// invoke this method more than N times, then writes will be in-bounds
947947
// and slots will not be initialized more than once.
948948
unsafe {
949949
self.array_mut.get_unchecked_mut(self.initialized).write(item);
@@ -972,7 +972,7 @@ impl<T: [const] Destruct> const Drop for Guard<'_, T> {
972972
/// `next` at most `N` times, the iterator can still be used afterwards to
973973
/// retrieve the remaining items.
974974
///
975-
/// If `iter.next()` panicks, all items already yielded by the iterator are
975+
/// If `iter.next()` panics, all items already yielded by the iterator are
976976
/// dropped.
977977
///
978978
/// Used for [`Iterator::next_chunk`].
@@ -1004,6 +1004,7 @@ fn iter_next_chunk_erased<T>(
10041004
buffer: &mut [MaybeUninit<T>],
10051005
iter: &mut impl Iterator<Item = T>,
10061006
) -> Result<(), usize> {
1007+
// if `Iterator::next` panics, this guard will drop already initialized items
10071008
let mut guard = Guard { array_mut: buffer, initialized: 0 };
10081009
while guard.initialized < guard.array_mut.len() {
10091010
let Some(item) = iter.next() else {

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: 91 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -4,185 +4,158 @@
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.
11+
// There are currently three flavors of how a C `va_list` is implemented for
12+
// targets that Rust supports:
1413
//
15-
// Most targets explicitly specify the layout of `va_list`, this layout is matched here.
14+
// - `va_list` is an opaque pointer
15+
// - `va_list` is a struct
16+
// - `va_list` is a single-element array, containing a struct
17+
//
18+
// The opaque pointer approach is the simplest to implement: the pointer just
19+
// points to an array of arguments on the caller's stack.
20+
//
21+
// The struct and single-element array variants are more complex, but
22+
// potentially more efficient because the additional state makes it
23+
// possible to pass variadic arguments via registers.
24+
//
25+
// The Rust `VaList` type is ABI-compatible with the C `va_list`.
26+
// The struct and pointer cases straightforwardly map to their Rust equivalents,
27+
// but the single-element array case is special: in C, this type is subject to
28+
// array-to-pointer decay.
29+
//
30+
// The `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute is used to match
31+
// the pointer decay behavior in Rust, while otherwise matching Rust semantics.
32+
// This attribute ensures that the compiler uses the correct ABI for functions
33+
// like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly.
1634
crate::cfg_select! {
1735
all(
1836
target_arch = "aarch64",
1937
not(target_vendor = "apple"),
2038
not(target_os = "uefi"),
2139
not(windows),
2240
) => {
23-
/// AArch64 ABI implementation of a `va_list`. See the
24-
/// [AArch64 Procedure Call Standard] for more details.
41+
/// AArch64 ABI implementation of a `va_list`.
42+
///
43+
/// See the [AArch64 Procedure Call Standard] for more details.
2544
///
2645
/// [AArch64 Procedure Call Standard]:
2746
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
2847
#[repr(C)]
2948
#[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,
49+
struct VaListInner {
50+
stack: *const c_void,
51+
gr_top: *const c_void,
52+
vr_top: *const c_void,
3553
gr_offs: i32,
3654
vr_offs: i32,
37-
_marker: PhantomInvariantLifetime<'f>,
3855
}
3956
}
4057
all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => {
4158
/// PowerPC ABI implementation of a `va_list`.
59+
///
60+
/// See the [LLVM source] and [GCC header] for more details.
61+
///
62+
/// [LLVM source]:
63+
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111
64+
/// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h
4265
#[repr(C)]
4366
#[derive(Debug)]
44-
#[lang = "va_list"]
45-
pub struct VaListImpl<'f> {
67+
#[rustc_pass_indirectly_in_non_rustic_abis]
68+
struct VaListInner {
4669
gpr: u8,
4770
fpr: u8,
4871
reserved: u16,
49-
overflow_arg_area: *mut c_void,
50-
reg_save_area: *mut c_void,
51-
_marker: PhantomInvariantLifetime<'f>,
72+
overflow_arg_area: *const c_void,
73+
reg_save_area: *const c_void,
5274
}
5375
}
5476
target_arch = "s390x" => {
5577
/// s390x ABI implementation of a `va_list`.
78+
///
79+
/// See the [S/390x ELF Application Binary Interface Supplement] for more details.
80+
///
81+
/// [S/390x ELF Application Binary Interface Supplement]:
82+
/// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf
5683
#[repr(C)]
5784
#[derive(Debug)]
58-
#[lang = "va_list"]
59-
pub struct VaListImpl<'f> {
85+
#[rustc_pass_indirectly_in_non_rustic_abis]
86+
struct VaListInner {
6087
gpr: i64,
6188
fpr: i64,
62-
overflow_arg_area: *mut c_void,
63-
reg_save_area: *mut c_void,
64-
_marker: PhantomInvariantLifetime<'f>,
89+
overflow_arg_area: *const c_void,
90+
reg_save_area: *const c_void,
6591
}
6692
}
6793
all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => {
68-
/// x86_64 ABI implementation of a `va_list`.
94+
/// x86_64 System V ABI implementation of a `va_list`.
95+
///
96+
/// See the [System V AMD64 ABI] for more details.
97+
///
98+
/// [System V AMD64 ABI]:
99+
/// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
69100
#[repr(C)]
70101
#[derive(Debug)]
71-
#[lang = "va_list"]
72-
pub struct VaListImpl<'f> {
102+
#[rustc_pass_indirectly_in_non_rustic_abis]
103+
struct VaListInner {
73104
gp_offset: i32,
74105
fp_offset: i32,
75-
overflow_arg_area: *mut c_void,
76-
reg_save_area: *mut c_void,
77-
_marker: PhantomInvariantLifetime<'f>,
106+
overflow_arg_area: *const c_void,
107+
reg_save_area: *const c_void,
78108
}
79109
}
80110
target_arch = "xtensa" => {
81111
/// Xtensa ABI implementation of a `va_list`.
112+
///
113+
/// See the [LLVM source] for more details.
114+
///
115+
/// [LLVM source]:
116+
/// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215
82117
#[repr(C)]
83118
#[derive(Debug)]
84-
#[lang = "va_list"]
85-
pub struct VaListImpl<'f> {
86-
stk: *mut i32,
87-
reg: *mut i32,
119+
#[rustc_pass_indirectly_in_non_rustic_abis]
120+
struct VaListInner {
121+
stk: *const i32,
122+
reg: *const i32,
88123
ndx: i32,
89-
_marker: PhantomInvariantLifetime<'f>,
90124
}
91125
}
92126

93127
// The fallback implementation, used for:
94128
//
95129
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
96130
// - windows
131+
// - powerpc64 & powerpc64le
97132
// - uefi
98-
// - any other target for which we don't specify the `VaListImpl` above
133+
// - any other target for which we don't specify the `VaListInner` above
99134
//
100135
// In this implementation the `va_list` type is just an alias for an opaque pointer.
101136
// That pointer is probably just the next variadic argument on the caller's stack.
102137
_ => {
103138
/// Basic implementation of a `va_list`.
104139
#[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)]
157140
#[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-
}
141+
struct VaListInner {
142+
ptr: *const c_void,
169143
}
170144
}
171145
}
172146

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-
}
147+
/// A variable argument list, equivalent to `va_list` in C.
148+
#[repr(transparent)]
149+
#[lang = "va_list"]
150+
pub struct VaList<'a> {
151+
inner: VaListInner,
152+
_marker: PhantomCovariantLifetime<'a>,
180153
}
181154

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
155+
impl fmt::Debug for VaList<'_> {
156+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157+
// No need to include `_marker` in debug output.
158+
f.debug_tuple("VaList").field(&self.inner).finish()
186159
}
187160
}
188161

@@ -203,7 +176,7 @@ mod sealed {
203176
impl<T> Sealed for *const T {}
204177
}
205178

206-
/// Types that are valid to read using [`VaListImpl::arg`].
179+
/// Types that are valid to read using [`VaList::arg`].
207180
///
208181
/// # Safety
209182
///
@@ -238,7 +211,7 @@ unsafe impl VaArgSafe for f64 {}
238211
unsafe impl<T> VaArgSafe for *mut T {}
239212
unsafe impl<T> VaArgSafe for *const T {}
240213

241-
impl<'f> VaListImpl<'f> {
214+
impl<'f> VaList<'f> {
242215
/// Advance to and read the next variable argument.
243216
///
244217
/// # Safety
@@ -258,46 +231,25 @@ impl<'f> VaListImpl<'f> {
258231
// SAFETY: the caller must uphold the safety contract for `va_arg`.
259232
unsafe { va_arg(self) }
260233
}
261-
262-
/// Copies the `va_list` at the current location.
263-
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
264-
where
265-
F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
266-
{
267-
let mut ap = self.clone();
268-
let ret = f(ap.as_va_list());
269-
// SAFETY: the caller must uphold the safety contract for `va_end`.
270-
unsafe {
271-
va_end(&mut ap);
272-
}
273-
ret
274-
}
275234
}
276235

277-
impl<'f> Clone for VaListImpl<'f> {
236+
impl<'f> Clone for VaList<'f> {
278237
#[inline]
279238
fn clone(&self) -> Self {
280239
let mut dest = crate::mem::MaybeUninit::uninit();
281-
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
240+
// SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
282241
unsafe {
283242
va_copy(dest.as_mut_ptr(), self);
284243
dest.assume_init()
285244
}
286245
}
287246
}
288247

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

0 commit comments

Comments
 (0)