5
5
use crate :: ffi:: c_void;
6
6
#[ allow( unused_imports) ]
7
7
use crate :: fmt;
8
- use crate :: marker:: { PhantomData , PhantomInvariantLifetime } ;
9
- use crate :: ops:: { Deref , DerefMut } ;
8
+ use crate :: marker:: PhantomInvariantLifetime ;
10
9
11
- // The name is WIP, using `VaListImpl` for now.
12
- //
13
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
+
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.
14
28
crate :: cfg_select! {
15
29
all(
16
30
target_arch = "aarch64" ,
@@ -26,7 +40,8 @@ crate::cfg_select! {
26
40
#[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
27
41
#[ derive( Debug ) ]
28
42
#[ lang = "va_list" ]
29
- pub struct VaListImpl <' f> {
43
+ #[ rustc_pass_indirectly_in_non_rustic_abis]
44
+ pub struct VaList <' f> {
30
45
stack: * mut c_void,
31
46
gr_top: * mut c_void,
32
47
vr_top: * mut c_void,
@@ -40,7 +55,8 @@ crate::cfg_select! {
40
55
#[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
41
56
#[ derive( Debug ) ]
42
57
#[ lang = "va_list" ]
43
- pub struct VaListImpl <' f> {
58
+ #[ rustc_pass_indirectly_in_non_rustic_abis]
59
+ pub struct VaList <' f> {
44
60
gpr: u8 ,
45
61
fpr: u8 ,
46
62
reserved: u16 ,
@@ -54,7 +70,8 @@ crate::cfg_select! {
54
70
#[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
55
71
#[ derive( Debug ) ]
56
72
#[ lang = "va_list" ]
57
- pub struct VaListImpl <' f> {
73
+ #[ rustc_pass_indirectly_in_non_rustic_abis]
74
+ pub struct VaList <' f> {
58
75
gpr: i64 ,
59
76
fpr: i64 ,
60
77
overflow_arg_area: * mut c_void,
@@ -67,7 +84,8 @@ crate::cfg_select! {
67
84
#[ cfg_attr( not( doc) , repr( C ) ) ] // work around https://github.com/rust-lang/rust/issues/66401
68
85
#[ derive( Debug ) ]
69
86
#[ lang = "va_list" ]
70
- pub struct VaListImpl <' f> {
87
+ #[ rustc_pass_indirectly_in_non_rustic_abis]
88
+ pub struct VaList <' f> {
71
89
gp_offset: i32 ,
72
90
fp_offset: i32 ,
73
91
overflow_arg_area: * mut c_void,
@@ -80,7 +98,8 @@ crate::cfg_select! {
80
98
#[ repr( C ) ]
81
99
#[ derive( Debug ) ]
82
100
#[ lang = "va_list" ]
83
- pub struct VaListImpl <' f> {
101
+ #[ rustc_pass_indirectly_in_non_rustic_abis]
102
+ pub struct VaList <' f> {
84
103
stk: * mut i32 ,
85
104
reg: * mut i32 ,
86
105
ndx: i32 ,
@@ -93,97 +112,30 @@ crate::cfg_select! {
93
112
// - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599)
94
113
// - windows
95
114
// - 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
97
116
//
98
117
// In this implementation the `va_list` type is just an alias for an opaque pointer.
99
118
// That pointer is probably just the next variadic argument on the caller's stack.
100
119
_ => {
101
120
/// Basic implementation of a `va_list`.
102
121
#[ repr( transparent) ]
103
122
#[ lang = "va_list" ]
104
- pub struct VaListImpl <' f> {
123
+ pub struct VaList <' f> {
105
124
ptr: * mut c_void,
106
125
107
- // Invariant over `'f`, so each `VaListImpl <'f>` object is tied to
126
+ // Invariant over `'f`, so each `VaList <'f>` object is tied to
108
127
// the region of the function it's defined in
109
128
_marker: PhantomInvariantLifetime <' f>,
110
129
}
111
130
112
- impl <' f> fmt:: Debug for VaListImpl <' f> {
131
+ impl <' f> fmt:: Debug for VaList <' f> {
113
132
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( )
166
134
}
167
135
}
168
136
}
169
137
}
170
138
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
-
187
139
mod sealed {
188
140
pub trait Sealed { }
189
141
@@ -201,7 +153,7 @@ mod sealed {
201
153
impl < T > Sealed for * const T { }
202
154
}
203
155
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`].
205
157
///
206
158
/// # Safety
207
159
///
@@ -231,69 +183,31 @@ unsafe impl VaArgSafe for f64 {}
231
183
unsafe impl < T > VaArgSafe for * mut T { }
232
184
unsafe impl < T > VaArgSafe for * const T { }
233
185
234
- impl < ' f > VaListImpl < ' f > {
186
+ impl < ' f > VaList < ' f > {
235
187
/// Advance to the next arg.
236
188
#[ inline]
237
189
pub unsafe fn arg < T : VaArgSafe > ( & mut self ) -> T {
238
190
// 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 ) }
254
192
}
255
193
}
256
194
257
- impl < ' f > Clone for VaListImpl < ' f > {
195
+ impl < ' f > Clone for VaList < ' f > {
258
196
#[ inline]
259
197
fn clone ( & self ) -> Self {
260
198
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.
262
200
unsafe {
263
- va_copy ( dest. as_mut_ptr ( ) , self ) ;
201
+ crate :: intrinsics :: va_copy ( dest. as_mut_ptr ( ) , self ) ;
264
202
dest. assume_init ( )
265
203
}
266
204
}
267
205
}
268
206
269
- impl < ' f > Drop for VaListImpl < ' f > {
207
+ impl < ' f > Drop for VaList < ' f > {
270
208
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.
281
212
}
282
213
}
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