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