55use crate :: ffi:: c_void;
66#[ allow( unused_imports) ]
77use crate :: fmt;
8- use crate :: intrinsics:: { va_arg, va_copy, va_end} ;
9- use crate :: marker:: { PhantomData , PhantomInvariantLifetime } ;
10- use crate :: ops:: { Deref , DerefMut } ;
8+ use crate :: intrinsics:: { va_arg, va_copy} ;
9+ use crate :: marker:: PhantomInvariantLifetime ;
1110
12- // The name is WIP, using `VaListImpl` for now.
13- //
1411// 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.
16+
17+ // Note that currently support for `#[rustc_pass_indirectly_in_non_rustic_abis]` is only implemented
18+ // on architectures which need it here, so when adding support for a new architecture the following
19+ // will need to happen:
20+ //
21+ // - Check that the calling conventions used on the new architecture correctly check
22+ // `arg.layout.pass_indirectly_in_non_rustic_abis()` and call `arg.make_indirect()` if it returns
23+ // `true`.
24+ // - Add a revision to the `tests/ui/abi/pass-indirectly-attr.rs` test for the new architecture.
25+ // - Add the new architecture to the `supported_architectures` array in the
26+ // `check_pass_indirectly_in_non_rustic_abis` function in
27+ // `compiler/rustc_passes/src/check_attr.rs`. This will stop the compiler from emitting an error
28+ // message when the attribute is used on that architecture.
1529crate :: cfg_select! {
1630 all(
1731 target_arch = "aarch64" ,
@@ -27,7 +41,8 @@ crate::cfg_select! {
2741 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
2842 #[ derive( Debug ) ]
2943 #[ lang = "va_list" ]
30- pub struct VaListImpl <' f> {
44+ #[ rustc_pass_indirectly_in_non_rustic_abis]
45+ pub struct VaList <' f> {
3146 stack: * mut c_void,
3247 gr_top: * mut c_void,
3348 vr_top: * mut c_void,
@@ -41,7 +56,8 @@ crate::cfg_select! {
4156 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
4257 #[ derive( Debug ) ]
4358 #[ lang = "va_list" ]
44- pub struct VaListImpl <' f> {
59+ #[ rustc_pass_indirectly_in_non_rustic_abis]
60+ pub struct VaList <' f> {
4561 gpr: u8 ,
4662 fpr: u8 ,
4763 reserved: u16 ,
@@ -55,7 +71,8 @@ crate::cfg_select! {
5571 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
5672 #[ derive( Debug ) ]
5773 #[ lang = "va_list" ]
58- pub struct VaListImpl <' f> {
74+ #[ rustc_pass_indirectly_in_non_rustic_abis]
75+ pub struct VaList <' f> {
5976 gpr: i64 ,
6077 fpr: i64 ,
6178 overflow_arg_area: * mut c_void,
@@ -68,7 +85,8 @@ crate::cfg_select! {
6885 #[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
6986 #[ derive( Debug ) ]
7087 #[ lang = "va_list" ]
71- pub struct VaListImpl <' f> {
88+ #[ rustc_pass_indirectly_in_non_rustic_abis]
89+ pub struct VaList <' f> {
7290 gp_offset: i32 ,
7391 fp_offset: i32 ,
7492 overflow_arg_area: * mut c_void,
@@ -81,7 +99,8 @@ crate::cfg_select! {
8199 #[ repr( C ) ]
82100 #[ derive( Debug ) ]
83101 #[ lang = "va_list" ]
84- pub struct VaListImpl <' f> {
102+ #[ rustc_pass_indirectly_in_non_rustic_abis]
103+ pub struct VaList <' f> {
85104 stk: * mut i32 ,
86105 reg: * mut i32 ,
87106 ndx: i32 ,
@@ -94,97 +113,30 @@ crate::cfg_select! {
94113 // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
95114 // - windows
96115 // - uefi
97- // - any other target for which we don't specify the `VaListImpl ` above
116+ // - any other target for which we don't specify the `VaList ` above
98117 //
99118 // In this implementation the `va_list` type is just an alias for an opaque pointer.
100119 // That pointer is probably just the next variadic argument on the caller's stack.
101120 _ => {
102121 /// Basic implementation of a `va_list`.
103122 #[ repr( transparent) ]
104123 #[ lang = "va_list" ]
105- pub struct VaListImpl <' f> {
124+ pub struct VaList <' f> {
106125 ptr: * mut c_void,
107126
108- // Invariant over `'f`, so each `VaListImpl <'f>` object is tied to
127+ // Invariant over `'f`, so each `VaList <'f>` object is tied to
109128 // the region of the function it's defined in
110129 _marker: PhantomInvariantLifetime <' f>,
111130 }
112131
113- impl <' f> fmt:: Debug for VaListImpl <' f> {
132+ impl <' f> fmt:: Debug for VaList <' f> {
114133 fn fmt( & self , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
115- write! ( f , "va_list* {:p}" , self . ptr)
134+ f . debug_tuple ( "VaList" ) . field ( & self . ptr) . finish ( )
116135 }
117136 }
118137 }
119138}
120139
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) ]
156- #[ 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- }
168- }
169- }
170- }
171-
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- }
179- }
180-
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
185- }
186- }
187-
188140mod sealed {
189141 pub trait Sealed { }
190142
@@ -202,7 +154,7 @@ mod sealed {
202154 impl < T > Sealed for * const T { }
203155}
204156
205- /// Trait which permits the allowed types to be used with [`VaListImpl ::arg`].
157+ /// Trait which permits the allowed types to be used with [`VaList ::arg`].
206158///
207159/// # Safety
208160///
@@ -232,52 +184,31 @@ unsafe impl VaArgSafe for f64 {}
232184unsafe impl < T > VaArgSafe for * mut T { }
233185unsafe impl < T > VaArgSafe for * const T { }
234186
235- impl < ' f > VaListImpl < ' f > {
187+ impl < ' f > VaList < ' f > {
236188 /// Advance to the next arg.
237189 #[ inline]
238190 pub unsafe fn arg < T : VaArgSafe > ( & mut self ) -> T {
239191 // SAFETY: the caller must uphold the safety contract for `va_arg`.
240192 unsafe { va_arg ( self ) }
241193 }
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- }
256194}
257195
258- impl < ' f > Clone for VaListImpl < ' f > {
196+ impl < ' f > Clone for VaList < ' f > {
259197 #[ inline]
260198 fn clone ( & self ) -> Self {
261199 let mut dest = crate :: mem:: MaybeUninit :: uninit ( ) ;
262- // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
200+ // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal.
263201 unsafe {
264202 va_copy ( dest. as_mut_ptr ( ) , self ) ;
265203 dest. assume_init ( )
266204 }
267205 }
268206}
269207
270- impl < ' f > Drop for VaListImpl < ' f > {
208+ impl < ' f > Drop for VaList < ' f > {
271209 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.
210+ // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour
211+ // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this
212+ // destructor is empty.
282213 }
283214}
0 commit comments