3
3
//! Better known as "varargs".
4
4
5
5
use crate :: ffi:: c_void;
6
- #[ allow( unused_imports) ]
7
6
use 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 ;
11
9
12
- // The name is WIP, using `VaListImpl` for now.
13
- //
14
10
// 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
15
crate :: cfg_select! {
16
16
all(
17
17
target_arch = "aarch64" ,
@@ -24,68 +24,63 @@ crate::cfg_select! {
24
24
///
25
25
/// [AArch64 Procedure Call Standard]:
26
26
/// 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 ) ]
28
28
#[ 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,
34
34
gr_offs: i32 ,
35
35
vr_offs: i32 ,
36
- _marker: PhantomInvariantLifetime <' f>,
37
36
}
38
37
}
39
38
all( target_arch = "powerpc" , not( target_os = "uefi" ) , not( windows) ) => {
40
39
/// 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 ) ]
42
41
#[ derive( Debug ) ]
43
- #[ lang = "va_list" ]
44
- pub struct VaListImpl < ' f> {
42
+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
43
+ struct VaListInner {
45
44
gpr: u8 ,
46
45
fpr: u8 ,
47
46
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,
51
49
}
52
50
}
53
51
target_arch = "s390x" => {
54
52
/// 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 ) ]
56
54
#[ derive( Debug ) ]
57
- #[ lang = "va_list" ]
58
- pub struct VaListImpl < ' f> {
55
+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
56
+ struct VaListInner {
59
57
gpr: i64 ,
60
58
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,
64
61
}
65
62
}
66
63
all( target_arch = "x86_64" , not( target_os = "uefi" ) , not( windows) ) => {
67
64
/// 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 ) ]
69
66
#[ derive( Debug ) ]
70
- #[ lang = "va_list" ]
71
- pub struct VaListImpl < ' f> {
67
+ #[ rustc_pass_indirectly_in_non_rustic_abis ]
68
+ struct VaListInner {
72
69
gp_offset: i32 ,
73
70
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,
77
73
}
78
74
}
79
75
target_arch = "xtensa" => {
80
76
/// Xtensa ABI implementation of a `va_list`.
81
77
#[ repr( C ) ]
82
78
#[ 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 ,
87
83
ndx: i32 ,
88
- _marker: PhantomInvariantLifetime <' f>,
89
84
}
90
85
}
91
86
@@ -94,94 +89,32 @@ crate::cfg_select! {
94
89
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
95
90
// - windows
96
91
// - 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
98
93
//
99
94
// In this implementation the `va_list` type is just an alias for an opaque pointer.
100
95
// That pointer is probably just the next variadic argument on the caller's stack.
101
96
_ => {
102
97
/// Basic implementation of a `va_list`.
103
98
#[ 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) ]
156
99
#[ 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,
168
102
}
169
103
}
170
104
}
171
105
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 > ,
179
112
}
180
113
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 ( )
185
118
}
186
119
}
187
120
@@ -202,7 +135,7 @@ mod sealed {
202
135
impl < T > Sealed for * const T { }
203
136
}
204
137
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`].
206
139
///
207
140
/// # Safety
208
141
///
@@ -232,52 +165,31 @@ unsafe impl VaArgSafe for f64 {}
232
165
unsafe impl < T > VaArgSafe for * mut T { }
233
166
unsafe impl < T > VaArgSafe for * const T { }
234
167
235
- impl < ' f > VaListImpl < ' f > {
168
+ impl < ' f > VaList < ' f > {
236
169
/// Advance to the next arg.
237
170
#[ inline]
238
171
pub unsafe fn arg < T : VaArgSafe > ( & mut self ) -> T {
239
172
// SAFETY: the caller must uphold the safety contract for `va_arg`.
240
173
unsafe { va_arg ( self ) }
241
174
}
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
- }
256
175
}
257
176
258
- impl < ' f > Clone for VaListImpl < ' f > {
177
+ impl < ' f > Clone for VaList < ' f > {
259
178
#[ inline]
260
179
fn clone ( & self ) -> Self {
261
180
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.
263
182
unsafe {
264
183
va_copy ( dest. as_mut_ptr ( ) , self ) ;
265
184
dest. assume_init ( )
266
185
}
267
186
}
268
187
}
269
188
270
- impl < ' f > Drop for VaListImpl < ' f > {
189
+ impl < ' f > Drop for VaList < ' f > {
271
190
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.
282
194
}
283
195
}
0 commit comments