55use crate :: ffi:: c_void;
66#[ allow( unused_imports) ]
77use crate :: fmt;
8- use crate :: marker:: { PhantomData , PhantomInvariantLifetime } ;
9- use crate :: ops:: { Deref , DerefMut } ;
8+ use crate :: marker:: PhantomInvariantLifetime ;
109
11- // The name is WIP, using `VaListImpl` for now.
12- //
1310// 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.
15+
16+ // Note that currently support for `#[rustc_pass_indirectly_in_non_rustic_abis]` is only implemented
17+ // on architectures which need it here, so when adding support for a new architecture the following
18+ // will need to happen:
19+ //
20+ // - Check that the calling conventions used on the new architecture correctly check
21+ // `arg.layout.pass_indirectly_in_non_rustic_abis()` and call `arg.make_indirect()` if it returns
22+ // `true`.
23+ // - Add a revision to the `tests/ui/abi/pass-indirectly-attr.rs` test for the new architecture.
24+ // - Add the new architecture to the `supported_architectures` array in the
25+ // `check_pass_indirectly_in_non_rustic_abis` function in
26+ // `compiler/rustc_passes/src/check_attr.rs`. This will stop the compiler from emitting an error
27+ // message when the attribute is used on that architecture.
1428crate :: cfg_select! {
1529 all(
1630 target_arch = "aarch64" ,
@@ -26,7 +40,8 @@ crate::cfg_select! {
2640 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
2741 #[ derive( Debug ) ]
2842 #[ lang = "va_list" ]
29- pub struct VaListImpl <' f> {
43+ #[ rustc_pass_indirectly_in_non_rustic_abis]
44+ pub struct VaList <' f> {
3045 stack: * mut c_void,
3146 gr_top: * mut c_void,
3247 vr_top: * mut c_void,
@@ -40,7 +55,8 @@ crate::cfg_select! {
4055 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
4156 #[ derive( Debug ) ]
4257 #[ lang = "va_list" ]
43- pub struct VaListImpl <' f> {
58+ #[ rustc_pass_indirectly_in_non_rustic_abis]
59+ pub struct VaList <' f> {
4460 gpr: u8 ,
4561 fpr: u8 ,
4662 reserved: u16 ,
@@ -54,7 +70,8 @@ crate::cfg_select! {
5470 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
5571 #[ derive( Debug ) ]
5672 #[ lang = "va_list" ]
57- pub struct VaListImpl <' f> {
73+ #[ rustc_pass_indirectly_in_non_rustic_abis]
74+ pub struct VaList <' f> {
5875 gpr: i64 ,
5976 fpr: i64 ,
6077 overflow_arg_area: * mut c_void,
@@ -67,7 +84,8 @@ crate::cfg_select! {
6784 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
6885 #[ derive( Debug ) ]
6986 #[ lang = "va_list" ]
70- pub struct VaListImpl <' f> {
87+ #[ rustc_pass_indirectly_in_non_rustic_abis]
88+ pub struct VaList <' f> {
7189 gp_offset: i32 ,
7290 fp_offset: i32 ,
7391 overflow_arg_area: * mut c_void,
@@ -80,7 +98,8 @@ crate::cfg_select! {
8098 #[ repr( C ) ]
8199 #[ derive( Debug ) ]
82100 #[ lang = "va_list" ]
83- pub struct VaListImpl <' f> {
101+ #[ rustc_pass_indirectly_in_non_rustic_abis]
102+ pub struct VaList <' f> {
84103 stk: * mut i32 ,
85104 reg: * mut i32 ,
86105 ndx: i32 ,
@@ -93,97 +112,30 @@ crate::cfg_select! {
93112 // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
94113 // - windows
95114 // - uefi
96- // - any other target for which we don't specify the `VaListImpl ` above
115+ // - any other target for which we don't specify the `VaList ` above
97116 //
98117 // In this implementation the `va_list` type is just an alias for an opaque pointer.
99118 // That pointer is probably just the next variadic argument on the caller's stack.
100119 _ => {
101120 /// Basic implementation of a `va_list`.
102121 #[ repr( transparent) ]
103122 #[ lang = "va_list" ]
104- pub struct VaListImpl <' f> {
123+ pub struct VaList <' f> {
105124 ptr: * mut c_void,
106125
107- // Invariant over `'f`, so each `VaListImpl <'f>` object is tied to
126+ // Invariant over `'f`, so each `VaList <'f>` object is tied to
108127 // the region of the function it's defined in
109128 _marker: PhantomInvariantLifetime <' f>,
110129 }
111130
112- impl <' f> fmt:: Debug for VaListImpl <' f> {
131+ impl <' f> fmt:: Debug for VaList <' f> {
113132 fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
114- write!( f, "va_list* {:p}" , self . ptr)
115- }
116- }
117- }
118- }
119-
120- crate :: cfg_select! {
121- all(
122- any(
123- target_arch = "aarch64" ,
124- target_arch = "powerpc" ,
125- target_arch = "s390x" ,
126- target_arch = "x86_64"
127- ) ,
128- not( target_arch = "xtensa" ) ,
129- any( not( target_arch = "aarch64" ) , not( target_vendor = "apple" ) ) ,
130- not( target_family = "wasm" ) ,
131- not( target_os = "uefi" ) ,
132- not( windows) ,
133- ) => {
134- /// A wrapper for a `va_list`
135- #[ repr( transparent) ]
136- #[ derive( Debug ) ]
137- pub struct VaList <' a, ' f: ' a> {
138- inner: & ' a mut VaListImpl <' f>,
139- _marker: PhantomData <& ' a mut VaListImpl <' f>>,
140- }
141-
142-
143- impl <' f> VaListImpl <' f> {
144- /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
145- #[ inline]
146- pub fn as_va_list<' a>( & ' a mut self ) -> VaList <' a, ' f> {
147- VaList { inner: self , _marker: PhantomData }
148- }
149- }
150- }
151-
152- _ => {
153- /// A wrapper for a `va_list`
154- #[ repr( transparent) ]
155- #[ derive( Debug ) ]
156- pub struct VaList <' a, ' f: ' a> {
157- inner: VaListImpl <' f>,
158- _marker: PhantomData <& ' a mut VaListImpl <' f>>,
159- }
160-
161- impl <' f> VaListImpl <' f> {
162- /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`.
163- #[ inline]
164- pub fn as_va_list<' a>( & ' a mut self ) -> VaList <' a, ' f> {
165- VaList { inner: VaListImpl { ..* self } , _marker: PhantomData }
133+ f. debug_tuple( "VaList" ) . field( & self . ptr) . finish( )
166134 }
167135 }
168136 }
169137}
170138
171- impl < ' a , ' f : ' a > Deref for VaList < ' a , ' f > {
172- type Target = VaListImpl < ' f > ;
173-
174- #[ inline]
175- fn deref ( & self ) -> & VaListImpl < ' f > {
176- & self . inner
177- }
178- }
179-
180- impl < ' a , ' f : ' a > DerefMut for VaList < ' a , ' f > {
181- #[ inline]
182- fn deref_mut ( & mut self ) -> & mut VaListImpl < ' f > {
183- & mut self . inner
184- }
185- }
186-
187139mod sealed {
188140 pub trait Sealed { }
189141
@@ -201,7 +153,7 @@ mod sealed {
201153 impl < T > Sealed for * const T { }
202154}
203155
204- /// Trait which permits the allowed types to be used with [`VaListImpl ::arg`].
156+ /// Trait which permits the allowed types to be used with [`VaList ::arg`].
205157///
206158/// # Safety
207159///
@@ -231,69 +183,31 @@ unsafe impl VaArgSafe for f64 {}
231183unsafe impl < T > VaArgSafe for * mut T { }
232184unsafe impl < T > VaArgSafe for * const T { }
233185
234- impl < ' f > VaListImpl < ' f > {
186+ impl < ' f > VaList < ' f > {
235187 /// Advance to the next arg.
236188 #[ inline]
237189 pub unsafe fn arg < T : VaArgSafe > ( & mut self ) -> T {
238190 // SAFETY: the caller must uphold the safety contract for `va_arg`.
239- unsafe { va_arg ( self ) }
240- }
241-
242- /// Copies the `va_list` at the current location.
243- pub unsafe fn with_copy < F , R > ( & self , f : F ) -> R
244- where
245- F : for < ' copy > FnOnce ( VaList < ' copy , ' f > ) -> R ,
246- {
247- let mut ap = self . clone ( ) ;
248- let ret = f ( ap. as_va_list ( ) ) ;
249- // SAFETY: the caller must uphold the safety contract for `va_end`.
250- unsafe {
251- va_end ( & mut ap) ;
252- }
253- ret
191+ unsafe { crate :: intrinsics:: va_arg ( self ) }
254192 }
255193}
256194
257- impl < ' f > Clone for VaListImpl < ' f > {
195+ impl < ' f > Clone for VaList < ' f > {
258196 #[ inline]
259197 fn clone ( & self ) -> Self {
260198 let mut dest = crate :: mem:: MaybeUninit :: uninit ( ) ;
261- // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
199+ // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
262200 unsafe {
263- va_copy ( dest. as_mut_ptr ( ) , self ) ;
201+ crate :: intrinsics :: va_copy ( dest. as_mut_ptr ( ) , self ) ;
264202 dest. assume_init ( )
265203 }
266204 }
267205}
268206
269- impl < ' f > Drop for VaListImpl < ' f > {
207+ impl < ' f > Drop for VaList < ' f > {
270208 fn drop ( & mut self ) {
271- // FIXME: this should call `va_end`, but there's no clean way to
272- // guarantee that `drop` always gets inlined into its caller,
273- // so the `va_end` would get directly called from the same function as
274- // the corresponding `va_copy`. `man va_end` states that C requires this,
275- // and LLVM basically follows the C semantics, so we need to make sure
276- // that `va_end` is always called from the same function as `va_copy`.
277- // For more details, see https://github.com/rust-lang/rust/pull/59625
278- // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
279- //
280- // This works for now, since `va_end` is a no-op on all current LLVM targets.
209+ // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
210+ // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
211+ // destructor is empty.
281212 }
282213}
283-
284- /// Destroy the arglist `ap` after initialization with `va_start` or
285- /// `va_copy`.
286- #[ rustc_intrinsic]
287- #[ rustc_nounwind]
288- unsafe fn va_end ( ap : & mut VaListImpl < ' _ > ) ;
289-
290- /// Copies the current location of arglist `src` to the arglist `dst`.
291- #[ rustc_intrinsic]
292- #[ rustc_nounwind]
293- unsafe fn va_copy < ' f > ( dest : * mut VaListImpl < ' f > , src : & VaListImpl < ' f > ) ;
294-
295- /// Loads an argument of type `T` from the `va_list` `ap` and increment the
296- /// argument `ap` points to.
297- #[ rustc_intrinsic]
298- #[ rustc_nounwind]
299- unsafe fn va_arg < T : VaArgSafe > ( ap : & mut VaListImpl < ' _ > ) -> T ;
0 commit comments