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