33//! Better known as "varargs".
44
55use crate :: ffi:: c_void;
6- #[ allow( unused_imports) ]
76use crate :: fmt;
8- use crate :: intrinsics:: { va_arg, va_copy, va_end} ;
9- use crate :: marker:: { PhantomData , PhantomInvariantLifetime } ;
10- use crate :: ops:: { Deref , DerefMut } ;
7+ use crate :: intrinsics:: { va_arg, va_copy} ;
8+ use crate :: marker:: PhantomCovariantLifetime ;
119
12- // The name is WIP, using `VaListImpl` for now.
13- //
1410// 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.
1515crate :: cfg_select! {
1616 all(
1717 target_arch = "aarch64" ,
@@ -24,68 +24,63 @@ crate::cfg_select! {
2424 ///
2525 /// [AArch64 Procedure Call Standard]:
2626 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
27- #[ cfg_attr ( not ( doc ) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
27+ #[ repr( C ) ]
2828 #[ derive( Debug ) ]
29- #[ lang = "va_list" ]
30- pub struct VaListImpl < ' f> {
31- stack: * mut c_void,
32- gr_top: * mut c_void,
33- vr_top: * mut c_void,
29+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
30+ struct VaListInner {
31+ stack: * const c_void,
32+ gr_top: * const c_void,
33+ vr_top: * const c_void,
3434 gr_offs: i32 ,
3535 vr_offs: i32 ,
36- _marker: PhantomInvariantLifetime <' f>,
3736 }
3837 }
3938 all( target_arch = "powerpc" , not( target_os = "uefi" ) , not( windows) ) => {
4039 /// PowerPC ABI implementation of a `va_list`.
41- #[ cfg_attr ( not ( doc ) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
40+ #[ repr( C ) ]
4241 #[ derive( Debug ) ]
43- #[ lang = "va_list" ]
44- pub struct VaListImpl < ' f> {
42+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
43+ struct VaListInner {
4544 gpr: u8 ,
4645 fpr: u8 ,
4746 reserved: u16 ,
48- overflow_arg_area: * mut c_void,
49- reg_save_area: * mut c_void,
50- _marker: PhantomInvariantLifetime <' f>,
47+ overflow_arg_area: * const c_void,
48+ reg_save_area: * const c_void,
5149 }
5250 }
5351 target_arch = "s390x" => {
5452 /// s390x ABI implementation of a `va_list`.
55- #[ cfg_attr ( not ( doc ) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
53+ #[ repr( C ) ]
5654 #[ derive( Debug ) ]
57- #[ lang = "va_list" ]
58- pub struct VaListImpl < ' f> {
55+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
56+ struct VaListInner {
5957 gpr: i64 ,
6058 fpr: i64 ,
61- overflow_arg_area: * mut c_void,
62- reg_save_area: * mut c_void,
63- _marker: PhantomInvariantLifetime <' f>,
59+ overflow_arg_area: * const c_void,
60+ reg_save_area: * const c_void,
6461 }
6562 }
6663 all( target_arch = "x86_64" , not( target_os = "uefi" ) , not( windows) ) => {
6764 /// x86_64 ABI implementation of a `va_list`.
68- #[ cfg_attr ( not ( doc ) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
65+ #[ repr( C ) ]
6966 #[ derive( Debug ) ]
70- #[ lang = "va_list" ]
71- pub struct VaListImpl < ' f> {
67+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
68+ struct VaListInner {
7269 gp_offset: i32 ,
7370 fp_offset: i32 ,
74- overflow_arg_area: * mut c_void,
75- reg_save_area: * mut c_void,
76- _marker: PhantomInvariantLifetime <' f>,
71+ overflow_arg_area: * const c_void,
72+ reg_save_area: * const c_void,
7773 }
7874 }
7975 target_arch = "xtensa" => {
8076 /// Xtensa ABI implementation of a `va_list`.
8177 #[ repr( C ) ]
8278 #[ derive( Debug ) ]
83- #[ lang = "va_list" ]
84- pub struct VaListImpl < ' f> {
85- stk: * mut i32 ,
86- reg: * mut i32 ,
79+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
80+ struct VaListInner {
81+ stk: * const i32 ,
82+ reg: * const i32 ,
8783 ndx: i32 ,
88- _marker: PhantomInvariantLifetime <' f>,
8984 }
9085 }
9186
@@ -94,94 +89,32 @@ crate::cfg_select! {
9489 // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
9590 // - windows
9691 // - uefi
97- // - 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
9893 //
9994 // In this implementation the `va_list` type is just an alias for an opaque pointer.
10095 // That pointer is probably just the next variadic argument on the caller's stack.
10196 _ => {
10297 /// Basic implementation of a `va_list`.
10398 #[ repr( transparent) ]
104- #[ lang = "va_list" ]
105- pub struct VaListImpl <' f> {
106- ptr: * mut c_void,
107-
108- // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
109- // the region of the function it's defined in
110- _marker: PhantomInvariantLifetime <' f>,
111- }
112-
113- impl <' f> fmt:: Debug for VaListImpl <' f> {
114- fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
115- write!( f, "va_list* {:p}" , self . ptr)
116- }
117- }
118- }
119- }
120-
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) ]
15699 #[ 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- }
100+ struct VaListInner {
101+ ptr: * const c_void,
168102 }
169103 }
170104}
171105
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- }
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 > ,
179112}
180113
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
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 ( )
185118 }
186119}
187120
@@ -202,7 +135,7 @@ mod sealed {
202135 impl < T > Sealed for * const T { }
203136}
204137
205- /// Trait which permits the allowed types to be used with [`VaListImpl ::arg`].
138+ /// Trait which permits the allowed types to be used with [`VaList ::arg`].
206139///
207140/// # Safety
208141///
@@ -232,52 +165,31 @@ unsafe impl VaArgSafe for f64 {}
232165unsafe impl < T > VaArgSafe for * mut T { }
233166unsafe impl < T > VaArgSafe for * const T { }
234167
235- impl < ' f > VaListImpl < ' f > {
168+ impl < ' f > VaList < ' f > {
236169 /// Advance to the next arg.
237170 #[ inline]
238171 pub unsafe fn arg < T : VaArgSafe > ( & mut self ) -> T {
239172 // SAFETY: the caller must uphold the safety contract for `va_arg`.
240173 unsafe { va_arg ( self ) }
241174 }
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- }
256175}
257176
258- impl < ' f > Clone for VaListImpl < ' f > {
177+ impl < ' f > Clone for VaList < ' f > {
259178 #[ inline]
260179 fn clone ( & self ) -> Self {
261180 let mut dest = crate :: mem:: MaybeUninit :: uninit ( ) ;
262- // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
181+ // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
263182 unsafe {
264183 va_copy ( dest. as_mut_ptr ( ) , self ) ;
265184 dest. assume_init ( )
266185 }
267186 }
268187}
269188
270- impl < ' f > Drop for VaListImpl < ' f > {
189+ impl < ' f > Drop for VaList < ' f > {
271190 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.
191+ // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
192+ // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
193+ // destructor is empty.
282194 }
283195}
0 commit comments