Skip to content

Commit 2db286b

Browse files
committed
Expose VaListImpl as the Rust equivalent of __va_list_tag and implement Clone and Drop for it.
1 parent fc45382 commit 2db286b

File tree

24 files changed

+376
-218
lines changed

24 files changed

+376
-218
lines changed

src/libcore/ffi.rs

Lines changed: 153 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! Utilities related to FFI bindings.
66
77
use crate::fmt;
8+
use crate::marker::PhantomData;
9+
use crate::ops::{Deref, DerefMut};
810

911
/// Equivalent to C's `void` type when used as a [pointer].
1012
///
@@ -45,25 +47,33 @@ impl fmt::Debug for c_void {
4547
}
4648

4749
/// Basic implementation of a `va_list`.
50+
// The name is WIP, using `VaListImpl` for now.
4851
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
4952
not(target_arch = "x86_64")),
5053
all(target_arch = "aarch64", target_os = "ios"),
5154
windows))]
55+
#[repr(transparent)]
5256
#[unstable(feature = "c_variadic",
5357
reason = "the `c_variadic` feature has not been properly tested on \
5458
all supported platforms",
5559
issue = "44930")]
56-
extern {
57-
type VaListImpl;
60+
#[lang = "va_list"]
61+
pub struct VaListImpl<'f> {
62+
ptr: *mut c_void,
63+
_marker: PhantomData<&'f c_void>,
5864
}
5965

6066
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
6167
not(target_arch = "x86_64")),
6268
all(target_arch = "aarch64", target_os = "ios"),
6369
windows))]
64-
impl fmt::Debug for VaListImpl {
70+
#[unstable(feature = "c_variadic",
71+
reason = "the `c_variadic` feature has not been properly tested on \
72+
all supported platforms",
73+
issue = "44930")]
74+
impl<'f> fmt::Debug for VaListImpl<'f> {
6575
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66-
write!(f, "va_list* {:p}", self)
76+
write!(f, "va_list* {:p}", self.ptr)
6777
}
6878
}
6979

@@ -79,12 +89,14 @@ impl fmt::Debug for VaListImpl {
7989
reason = "the `c_variadic` feature has not been properly tested on \
8090
all supported platforms",
8191
issue = "44930")]
82-
struct VaListImpl {
92+
#[lang = "va_list"]
93+
pub struct VaListImpl<'f> {
8394
stack: *mut c_void,
8495
gr_top: *mut c_void,
8596
vr_top: *mut c_void,
8697
gr_offs: i32,
8798
vr_offs: i32,
99+
_marker: PhantomData<&'f c_void>,
88100
}
89101

90102
/// PowerPC ABI implementation of a `va_list`.
@@ -95,12 +107,14 @@ struct VaListImpl {
95107
reason = "the `c_variadic` feature has not been properly tested on \
96108
all supported platforms",
97109
issue = "44930")]
98-
struct VaListImpl {
110+
#[lang = "va_list"]
111+
pub struct VaListImpl<'f> {
99112
gpr: u8,
100113
fpr: u8,
101114
reserved: u16,
102115
overflow_arg_area: *mut c_void,
103116
reg_save_area: *mut c_void,
117+
_marker: PhantomData<&'f c_void>,
104118
}
105119

106120
/// x86_64 ABI implementation of a `va_list`.
@@ -111,22 +125,99 @@ struct VaListImpl {
111125
reason = "the `c_variadic` feature has not been properly tested on \
112126
all supported platforms",
113127
issue = "44930")]
114-
struct VaListImpl {
128+
#[lang = "va_list"]
129+
pub struct VaListImpl<'f> {
115130
gp_offset: i32,
116131
fp_offset: i32,
117132
overflow_arg_area: *mut c_void,
118133
reg_save_area: *mut c_void,
134+
_marker: PhantomData<&'f c_void>,
119135
}
120136

121137
/// A wrapper for a `va_list`
122-
#[lang = "va_list"]
138+
#[repr(transparent)]
123139
#[derive(Debug)]
124140
#[unstable(feature = "c_variadic",
125141
reason = "the `c_variadic` feature has not been properly tested on \
126142
all supported platforms",
127143
issue = "44930")]
128-
#[repr(transparent)]
129-
pub struct VaList<'a>(&'a mut VaListImpl);
144+
pub struct VaList<'a, 'f: 'a> {
145+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
146+
not(target_arch = "x86_64")),
147+
all(target_arch = "aarch64", target_os = "ios"),
148+
windows))]
149+
inner: VaListImpl<'f>,
150+
151+
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
152+
target_arch = "x86_64"),
153+
any(not(target_arch = "aarch64"), not(target_os = "ios")),
154+
not(windows)))]
155+
inner: &'a mut VaListImpl<'f>,
156+
157+
_marker: PhantomData<&'a mut VaListImpl<'f>>,
158+
}
159+
160+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
161+
not(target_arch = "x86_64")),
162+
all(target_arch = "aarch64", target_os = "ios"),
163+
windows))]
164+
#[unstable(feature = "c_variadic",
165+
reason = "the `c_variadic` feature has not been properly tested on \
166+
all supported platforms",
167+
issue = "44930")]
168+
impl<'f> VaListImpl<'f> {
169+
/// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
170+
#[inline]
171+
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
172+
VaList {
173+
inner: VaListImpl { ..*self },
174+
_marker: PhantomData,
175+
}
176+
}
177+
}
178+
179+
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
180+
target_arch = "x86_64"),
181+
any(not(target_arch = "aarch64"), not(target_os = "ios")),
182+
not(windows)))]
183+
#[unstable(feature = "c_variadic",
184+
reason = "the `c_variadic` feature has not been properly tested on \
185+
all supported platforms",
186+
issue = "44930")]
187+
impl<'f> VaListImpl<'f> {
188+
/// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
189+
#[inline]
190+
pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
191+
VaList {
192+
inner: self,
193+
_marker: PhantomData,
194+
}
195+
}
196+
}
197+
198+
#[unstable(feature = "c_variadic",
199+
reason = "the `c_variadic` feature has not been properly tested on \
200+
all supported platforms",
201+
issue = "44930")]
202+
impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
203+
type Target = VaListImpl<'f>;
204+
205+
#[inline]
206+
fn deref(&self) -> &VaListImpl<'f> {
207+
&self.inner
208+
}
209+
}
210+
211+
#[unstable(feature = "c_variadic",
212+
reason = "the `c_variadic` feature has not been properly tested on \
213+
all supported platforms",
214+
issue = "44930")]
215+
impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
216+
#[inline]
217+
fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
218+
&mut self.inner
219+
}
220+
}
130221

131222
// The VaArgSafe trait needs to be used in public interfaces, however, the trait
132223
// itself must not be allowed to be used outside this module. Allowing users to
@@ -175,56 +266,76 @@ impl<T> sealed_trait::VaArgSafe for *mut T {}
175266
issue = "44930")]
176267
impl<T> sealed_trait::VaArgSafe for *const T {}
177268

178-
impl<'a> VaList<'a> {
269+
#[unstable(feature = "c_variadic",
270+
reason = "the `c_variadic` feature has not been properly tested on \
271+
all supported platforms",
272+
issue = "44930")]
273+
#[cfg(not(stage0))]
274+
impl<'f> VaListImpl<'f> {
179275
/// Advance to the next arg.
180-
#[unstable(feature = "c_variadic",
181-
reason = "the `c_variadic` feature has not been properly tested on \
182-
all supported platforms",
183-
issue = "44930")]
276+
#[inline]
184277
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
185278
va_arg(self)
186279
}
187280

188281
/// Copies the `va_list` at the current location.
189-
#[unstable(feature = "c_variadic",
190-
reason = "the `c_variadic` feature has not been properly tested on \
191-
all supported platforms",
192-
issue = "44930")]
193282
pub unsafe fn with_copy<F, R>(&self, f: F) -> R
194-
where F: for<'copy> FnOnce(VaList<'copy>) -> R {
195-
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
196-
not(target_arch = "x86_64")),
197-
all(target_arch = "aarch64", target_os = "ios"),
198-
windows))]
199-
let mut ap = va_copy(self);
200-
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
201-
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
202-
let mut ap_inner = va_copy(self);
203-
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
204-
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
205-
let mut ap = VaList(&mut ap_inner);
206-
let ret = f(VaList(ap.0));
283+
where F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R {
284+
let mut ap = self.clone();
285+
let ret = f(ap.as_va_list());
207286
va_end(&mut ap);
208287
ret
209288
}
210289
}
211290

291+
#[unstable(feature = "c_variadic",
292+
reason = "the `c_variadic` feature has not been properly tested on \
293+
all supported platforms",
294+
issue = "44930")]
295+
#[cfg(not(stage0))]
296+
impl<'f> Clone for VaListImpl<'f> {
297+
#[inline]
298+
fn clone(&self) -> Self {
299+
let mut dest = crate::mem::MaybeUninit::uninit();
300+
unsafe {
301+
va_copy(dest.as_mut_ptr(), self);
302+
dest.assume_init()
303+
}
304+
}
305+
}
306+
307+
#[unstable(feature = "c_variadic",
308+
reason = "the `c_variadic` feature has not been properly tested on \
309+
all supported platforms",
310+
issue = "44930")]
311+
#[cfg(not(stage0))]
312+
impl<'f> Drop for VaListImpl<'f> {
313+
fn drop(&mut self) {
314+
// FIXME: this should call `va_end`, but there's no clean way to
315+
// guarantee that `drop` always gets inlined into its caller,
316+
// so the `va_end` would get directly called from the same function as
317+
// the corresponding `va_copy`. `man va_end` states that C requires this,
318+
// and LLVM basically follows the C semantics, so we need to make sure
319+
// that `va_end` is always called from the same function as `va_copy`.
320+
// For more details, see https://github.com/rust-lang/rust/pull/59625
321+
// and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
322+
//
323+
// This works for now, since `va_end` is a no-op on all current LLVM targets.
324+
}
325+
}
326+
212327
extern "rust-intrinsic" {
213328
/// Destroy the arglist `ap` after initialization with `va_start` or
214329
/// `va_copy`.
215-
fn va_end(ap: &mut VaList<'_>);
330+
#[cfg(not(stage0))]
331+
fn va_end(ap: &mut VaListImpl<'_>);
216332

217333
/// Copies the current location of arglist `src` to the arglist `dst`.
218-
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
219-
not(target_arch = "x86_64")),
220-
all(target_arch = "aarch64", target_os = "ios"),
221-
windows))]
222-
fn va_copy<'a>(src: &VaList<'a>) -> VaList<'a>;
223-
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
224-
not(windows), not(all(target_arch = "aarch64", target_os = "ios"))))]
225-
fn va_copy(src: &VaList<'_>) -> VaListImpl;
334+
#[cfg(not(stage0))]
335+
fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
226336

227337
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
228338
/// argument `ap` points to.
229-
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaList<'_>) -> T;
339+
#[cfg(not(stage0))]
340+
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
230341
}

src/librustc/hir/lowering.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,7 @@ impl<'a> LoweringContext<'a> {
15251525
}
15261526
TyKind::Mac(_) => panic!("TyMac should have been expanded by now."),
15271527
TyKind::CVarArgs => {
1528-
// Create the implicit lifetime of the "spoofed" `VaList`.
1528+
// Create the implicit lifetime of the "spoofed" `VaListImpl`.
15291529
let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
15301530
let lt = self.new_implicit_lifetime(span);
15311531
hir::TyKind::CVarArgs(lt)

src/librustc/hir/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1882,7 +1882,7 @@ pub enum TyKind {
18821882
Infer,
18831883
/// Placeholder for a type that has failed to be defined.
18841884
Err,
1885-
/// Placeholder for C-variadic arguments. We "spoof" the `VaList` created
1885+
/// Placeholder for C-variadic arguments. We "spoof" the `VaListImpl` created
18861886
/// from the variadic arguments. This type is only valid up to typeck.
18871887
CVarArgs(Lifetime),
18881888
}

src/librustc/ty/layout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,7 +2501,7 @@ where
25012501
}
25022502

25032503
// If this is a C-variadic function, this is not the return value,
2504-
// and there is one or more fixed arguments; ensure that the `VaList`
2504+
// and there is one or more fixed arguments; ensure that the `VaListImpl`
25052505
// is ignored as an argument.
25062506
if sig.c_variadic {
25072507
match (last_arg_idx, arg_idx) {
@@ -2512,7 +2512,7 @@ where
25122512
};
25132513
match ty.sty {
25142514
ty::Adt(def, _) if def.did == va_list_did => {
2515-
// This is the "spoofed" `VaList`. Set the arguments mode
2515+
// This is the "spoofed" `VaListImpl`. Set the arguments mode
25162516
// so that it will be ignored.
25172517
arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
25182518
}

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
143143
self.va_end(args[0].immediate())
144144
}
145145
"va_copy" => {
146-
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
147-
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
148-
(Some(_), _) => self.load(args[0].immediate(),
149-
tcx.data_layout.pointer_align.abi),
150-
(None, _) => bug!("`va_list` language item must be defined")
151-
};
152146
let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
153-
self.call(intrinsic, &[llresult, va_list], None);
154-
return;
147+
self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None)
155148
}
156149
"va_arg" => {
157150
match fn_ty.ret.layout.abi {
@@ -718,37 +711,12 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
718711
self.call(expect, &[cond, self.const_bool(expected)], None)
719712
}
720713

721-
fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
722-
let target = &self.cx.tcx.sess.target.target;
723-
let arch = &target.arch;
724-
// A pointer to the architecture specific structure is passed to this
725-
// function. For pointer variants (i686, RISC-V, Windows, etc), we
726-
// should do do nothing, as the address to the pointer is needed. For
727-
// architectures with a architecture specific structure (`Aarch64`,
728-
// `X86_64`, etc), this function should load the structure from the
729-
// address provided.
730-
let va_list = match &**arch {
731-
_ if target.options.is_like_windows => list,
732-
"aarch64" if target.target_os == "ios" => list,
733-
"aarch64" | "x86_64" | "powerpc" =>
734-
self.load(list, self.tcx().data_layout.pointer_align.abi),
735-
_ => list,
736-
};
714+
fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
737715
let intrinsic = self.cx().get_intrinsic("llvm.va_start");
738716
self.call(intrinsic, &[va_list], None)
739717
}
740718

741-
fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
742-
let target = &self.cx.tcx.sess.target.target;
743-
let arch = &target.arch;
744-
// See the comment in `va_start` for the purpose of the following.
745-
let va_list = match &**arch {
746-
_ if target.options.is_like_windows => list,
747-
"aarch64" if target.target_os == "ios" => list,
748-
"aarch64" | "x86_64" | "powerpc" =>
749-
self.load(list, self.tcx().data_layout.pointer_align.abi),
750-
_ => list,
751-
};
719+
fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
752720
let intrinsic = self.cx().get_intrinsic("llvm.va_end");
753721
self.call(intrinsic, &[va_list], None)
754722
}

0 commit comments

Comments
 (0)