44
55#[ cfg( not( target_arch = "xtensa" ) ) ]
66use crate :: ffi:: c_void;
7- #[ allow( unused_imports) ]
87use 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.
14- //
1511// 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.
1616crate :: cfg_select! {
1717 all(
1818 target_arch = "aarch64" ,
@@ -27,66 +27,60 @@ crate::cfg_select! {
2727 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
2828 #[ repr( C ) ]
2929 #[ 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,
30+ struct VaListInner {
31+ stack: * const c_void,
32+ gr_top: * const c_void,
33+ vr_top: * const c_void,
3534 gr_offs: i32 ,
3635 vr_offs: i32 ,
37- _marker: PhantomInvariantLifetime <' f>,
3836 }
3937 }
4038 all( target_arch = "powerpc" , not( target_os = "uefi" ) , not( windows) ) => {
4139 /// PowerPC ABI implementation of a `va_list`.
4240 #[ repr( C ) ]
4341 #[ derive( Debug ) ]
44- #[ lang = "va_list" ]
45- pub struct VaListImpl < ' f> {
42+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
43+ struct VaListInner {
4644 gpr: u8 ,
4745 fpr: u8 ,
4846 reserved: u16 ,
49- overflow_arg_area: * mut c_void,
50- reg_save_area: * mut c_void,
51- _marker: PhantomInvariantLifetime <' f>,
47+ overflow_arg_area: * const c_void,
48+ reg_save_area: * const c_void,
5249 }
5350 }
5451 target_arch = "s390x" => {
5552 /// s390x ABI implementation of a `va_list`.
5653 #[ repr( C ) ]
5754 #[ derive( Debug ) ]
58- #[ lang = "va_list" ]
59- pub struct VaListImpl < ' f> {
55+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
56+ struct VaListInner {
6057 gpr: i64 ,
6158 fpr: i64 ,
62- overflow_arg_area: * mut c_void,
63- reg_save_area: * mut c_void,
64- _marker: PhantomInvariantLifetime <' f>,
59+ overflow_arg_area: * const c_void,
60+ reg_save_area: * const c_void,
6561 }
6662 }
6763 all( target_arch = "x86_64" , not( target_os = "uefi" ) , not( windows) ) => {
6864 /// x86_64 ABI implementation of a `va_list`.
6965 #[ repr( C ) ]
7066 #[ derive( Debug ) ]
71- #[ lang = "va_list" ]
72- pub struct VaListImpl < ' f> {
67+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
68+ struct VaListInner {
7369 gp_offset: i32 ,
7470 fp_offset: i32 ,
75- overflow_arg_area: * mut c_void,
76- reg_save_area: * mut c_void,
77- _marker: PhantomInvariantLifetime <' f>,
71+ overflow_arg_area: * const c_void,
72+ reg_save_area: * const c_void,
7873 }
7974 }
8075 target_arch = "xtensa" => {
8176 /// Xtensa ABI implementation of a `va_list`.
8277 #[ repr( C ) ]
8378 #[ derive( Debug ) ]
84- #[ lang = "va_list" ]
85- pub struct VaListImpl < ' f> {
86- stk: * mut i32 ,
87- reg: * mut i32 ,
79+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
80+ struct VaListInner {
81+ stk: * const i32 ,
82+ reg: * const i32 ,
8883 ndx: i32 ,
89- _marker: PhantomInvariantLifetime <' f>,
9084 }
9185 }
9286
@@ -95,94 +89,32 @@ crate::cfg_select! {
9589 // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
9690 // - windows
9791 // - uefi
98- // - 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
9993 //
10094 // In this implementation the `va_list` type is just an alias for an opaque pointer.
10195 // That pointer is probably just the next variadic argument on the caller's stack.
10296 _ => {
10397 /// Basic implementation of a `va_list`.
10498 #[ 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) ]
15799 #[ 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- }
100+ struct VaListInner {
101+ ptr: * const c_void,
169102 }
170103 }
171104}
172105
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- }
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 > ,
180112}
181113
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
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 ( )
186118 }
187119}
188120
@@ -203,7 +135,7 @@ mod sealed {
203135 impl < T > Sealed for * const T { }
204136}
205137
206- /// Types that are valid to read using [`VaListImpl ::arg`].
138+ /// Types that are valid to read using [`VaList ::arg`].
207139///
208140/// # Safety
209141///
@@ -238,7 +170,7 @@ unsafe impl VaArgSafe for f64 {}
238170unsafe impl < T > VaArgSafe for * mut T { }
239171unsafe impl < T > VaArgSafe for * const T { }
240172
241- impl < ' f > VaListImpl < ' f > {
173+ impl < ' f > VaList < ' f > {
242174 /// Advance to and read the next variable argument.
243175 ///
244176 /// # Safety
@@ -257,46 +189,25 @@ impl<'f> VaListImpl<'f> {
257189 // SAFETY: the caller must uphold the safety contract for `va_arg`.
258190 unsafe { va_arg ( self ) }
259191 }
260-
261- /// Copies the `va_list` at the current location.
262- pub unsafe fn with_copy < F , R > ( & self , f : F ) -> R
263- where
264- F : for < ' copy > FnOnce ( VaList < ' copy , ' f > ) -> R ,
265- {
266- let mut ap = self . clone ( ) ;
267- let ret = f ( ap. as_va_list ( ) ) ;
268- // SAFETY: the caller must uphold the safety contract for `va_end`.
269- unsafe {
270- va_end ( & mut ap) ;
271- }
272- ret
273- }
274192}
275193
276- impl < ' f > Clone for VaListImpl < ' f > {
194+ impl < ' f > Clone for VaList < ' f > {
277195 #[ inline]
278196 fn clone ( & self ) -> Self {
279197 let mut dest = crate :: mem:: MaybeUninit :: uninit ( ) ;
280- // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
198+ // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
281199 unsafe {
282200 va_copy ( dest. as_mut_ptr ( ) , self ) ;
283201 dest. assume_init ( )
284202 }
285203 }
286204}
287205
288- impl < ' f > Drop for VaListImpl < ' f > {
206+ impl < ' f > Drop for VaList < ' f > {
289207 fn drop ( & mut self ) {
290- // FIXME: this should call `va_end`, but there's no clean way to
291- // guarantee that `drop` always gets inlined into its caller,
292- // so the `va_end` would get directly called from the same function as
293- // the corresponding `va_copy`. `man va_end` states that C requires this,
294- // and LLVM basically follows the C semantics, so we need to make sure
295- // that `va_end` is always called from the same function as `va_copy`.
296- // For more details, see https://github.com/rust-lang/rust/pull/59625
297- // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
298- //
299- // This works for now, since `va_end` is a no-op on all current LLVM targets.
208+ // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
209+ // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
210+ // destructor is empty.
300211 }
301212}
302213
0 commit comments