]> git.lizzy.rs Git - rust.git/blob - library/core/src/ffi.rs
Add a copy of cfg_if to core's internal_macros.rs
[rust.git] / library / core / src / ffi.rs
1 #![stable(feature = "", since = "1.30.0")]
2 #![allow(non_camel_case_types)]
3
4 //! Utilities related to foreign function interface (FFI) bindings.
5
6 use crate::fmt;
7 use crate::marker::PhantomData;
8 use crate::ops::{Deref, DerefMut};
9
10 /// Equivalent to C's `void` type when used as a [pointer].
11 ///
12 /// In essence, `*const c_void` is equivalent to C's `const void*`
13 /// and `*mut c_void` is equivalent to C's `void*`. That said, this is
14 /// *not* the same as C's `void` return type, which is Rust's `()` type.
15 ///
16 /// To model pointers to opaque types in FFI, until `extern type` is
17 /// stabilized, it is recommended to use a newtype wrapper around an empty
18 /// byte array. See the [Nomicon] for details.
19 ///
20 /// One could use `std::os::raw::c_void` if they want to support old Rust
21 /// compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by
22 /// this definition. For more information, please read [RFC 2521].
23 ///
24 /// [Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
25 /// [RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md
26 // N.B., for LLVM to recognize the void pointer type and by extension
27 //     functions like malloc(), we need to have it represented as i8* in
28 //     LLVM bitcode. The enum used here ensures this and prevents misuse
29 //     of the "raw" type by only having private variants. We need two
30 //     variants, because the compiler complains about the repr attribute
31 //     otherwise and we need at least one variant as otherwise the enum
32 //     would be uninhabited and at least dereferencing such pointers would
33 //     be UB.
34 #[repr(u8)]
35 #[stable(feature = "core_c_void", since = "1.30.0")]
36 pub enum c_void {
37     #[unstable(
38         feature = "c_void_variant",
39         reason = "temporary implementation detail",
40         issue = "none"
41     )]
42     #[doc(hidden)]
43     __variant1,
44     #[unstable(
45         feature = "c_void_variant",
46         reason = "temporary implementation detail",
47         issue = "none"
48     )]
49     #[doc(hidden)]
50     __variant2,
51 }
52
53 #[stable(feature = "std_debug", since = "1.16.0")]
54 impl fmt::Debug for c_void {
55     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56         f.debug_struct("c_void").finish()
57     }
58 }
59
60 /// Basic implementation of a `va_list`.
61 // The name is WIP, using `VaListImpl` for now.
62 #[cfg(any(
63     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
64     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
65     target_family = "wasm",
66     target_arch = "asmjs",
67     windows
68 ))]
69 #[repr(transparent)]
70 #[unstable(
71     feature = "c_variadic",
72     reason = "the `c_variadic` feature has not been properly tested on \
73               all supported platforms",
74     issue = "44930"
75 )]
76 #[lang = "va_list"]
77 pub struct VaListImpl<'f> {
78     ptr: *mut c_void,
79
80     // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
81     // the region of the function it's defined in
82     _marker: PhantomData<&'f mut &'f c_void>,
83 }
84
85 #[cfg(any(
86     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
87     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
88     target_family = "wasm",
89     target_arch = "asmjs",
90     windows
91 ))]
92 #[unstable(
93     feature = "c_variadic",
94     reason = "the `c_variadic` feature has not been properly tested on \
95               all supported platforms",
96     issue = "44930"
97 )]
98 impl<'f> fmt::Debug for VaListImpl<'f> {
99     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100         write!(f, "va_list* {:p}", self.ptr)
101     }
102 }
103
104 /// AArch64 ABI implementation of a `va_list`. See the
105 /// [AArch64 Procedure Call Standard] for more details.
106 ///
107 /// [AArch64 Procedure Call Standard]:
108 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
109 #[cfg(all(
110     target_arch = "aarch64",
111     not(any(target_os = "macos", target_os = "ios")),
112     not(windows)
113 ))]
114 #[repr(C)]
115 #[derive(Debug)]
116 #[unstable(
117     feature = "c_variadic",
118     reason = "the `c_variadic` feature has not been properly tested on \
119               all supported platforms",
120     issue = "44930"
121 )]
122 #[lang = "va_list"]
123 pub struct VaListImpl<'f> {
124     stack: *mut c_void,
125     gr_top: *mut c_void,
126     vr_top: *mut c_void,
127     gr_offs: i32,
128     vr_offs: i32,
129     _marker: PhantomData<&'f mut &'f c_void>,
130 }
131
132 /// PowerPC ABI implementation of a `va_list`.
133 #[cfg(all(target_arch = "powerpc", not(windows)))]
134 #[repr(C)]
135 #[derive(Debug)]
136 #[unstable(
137     feature = "c_variadic",
138     reason = "the `c_variadic` feature has not been properly tested on \
139               all supported platforms",
140     issue = "44930"
141 )]
142 #[lang = "va_list"]
143 pub struct VaListImpl<'f> {
144     gpr: u8,
145     fpr: u8,
146     reserved: u16,
147     overflow_arg_area: *mut c_void,
148     reg_save_area: *mut c_void,
149     _marker: PhantomData<&'f mut &'f c_void>,
150 }
151
152 /// x86_64 ABI implementation of a `va_list`.
153 #[cfg(all(target_arch = "x86_64", not(windows)))]
154 #[repr(C)]
155 #[derive(Debug)]
156 #[unstable(
157     feature = "c_variadic",
158     reason = "the `c_variadic` feature has not been properly tested on \
159               all supported platforms",
160     issue = "44930"
161 )]
162 #[lang = "va_list"]
163 pub struct VaListImpl<'f> {
164     gp_offset: i32,
165     fp_offset: i32,
166     overflow_arg_area: *mut c_void,
167     reg_save_area: *mut c_void,
168     _marker: PhantomData<&'f mut &'f c_void>,
169 }
170
171 /// A wrapper for a `va_list`
172 #[repr(transparent)]
173 #[derive(Debug)]
174 #[unstable(
175     feature = "c_variadic",
176     reason = "the `c_variadic` feature has not been properly tested on \
177               all supported platforms",
178     issue = "44930"
179 )]
180 pub struct VaList<'a, 'f: 'a> {
181     #[cfg(any(
182         all(
183             not(target_arch = "aarch64"),
184             not(target_arch = "powerpc"),
185             not(target_arch = "x86_64")
186         ),
187         all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
188         target_family = "wasm",
189         target_arch = "asmjs",
190         windows
191     ))]
192     inner: VaListImpl<'f>,
193
194     #[cfg(all(
195         any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
196         any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
197         not(target_family = "wasm"),
198         not(target_arch = "asmjs"),
199         not(windows)
200     ))]
201     inner: &'a mut VaListImpl<'f>,
202
203     _marker: PhantomData<&'a mut VaListImpl<'f>>,
204 }
205
206 #[cfg(any(
207     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
208     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
209     target_family = "wasm",
210     target_arch = "asmjs",
211     windows
212 ))]
213 #[unstable(
214     feature = "c_variadic",
215     reason = "the `c_variadic` feature has not been properly tested on \
216               all supported platforms",
217     issue = "44930"
218 )]
219 impl<'f> VaListImpl<'f> {
220     /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
221     #[inline]
222     pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
223         VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
224     }
225 }
226
227 #[cfg(all(
228     any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
229     any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
230     not(target_family = "wasm"),
231     not(target_arch = "asmjs"),
232     not(windows)
233 ))]
234 #[unstable(
235     feature = "c_variadic",
236     reason = "the `c_variadic` feature has not been properly tested on \
237               all supported platforms",
238     issue = "44930"
239 )]
240 impl<'f> VaListImpl<'f> {
241     /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
242     #[inline]
243     pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
244         VaList { inner: self, _marker: PhantomData }
245     }
246 }
247
248 #[unstable(
249     feature = "c_variadic",
250     reason = "the `c_variadic` feature has not been properly tested on \
251               all supported platforms",
252     issue = "44930"
253 )]
254 impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
255     type Target = VaListImpl<'f>;
256
257     #[inline]
258     fn deref(&self) -> &VaListImpl<'f> {
259         &self.inner
260     }
261 }
262
263 #[unstable(
264     feature = "c_variadic",
265     reason = "the `c_variadic` feature has not been properly tested on \
266               all supported platforms",
267     issue = "44930"
268 )]
269 impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
270     #[inline]
271     fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
272         &mut self.inner
273     }
274 }
275
276 // The VaArgSafe trait needs to be used in public interfaces, however, the trait
277 // itself must not be allowed to be used outside this module. Allowing users to
278 // implement the trait for a new type (thereby allowing the va_arg intrinsic to
279 // be used on a new type) is likely to cause undefined behavior.
280 //
281 // FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
282 // but also ensure it cannot be used elsewhere, the trait needs to be public
283 // within a private module. Once RFC 2145 has been implemented look into
284 // improving this.
285 mod sealed_trait {
286     /// Trait which permits the allowed types to be used with [super::VaListImpl::arg].
287     #[unstable(
288         feature = "c_variadic",
289         reason = "the `c_variadic` feature has not been properly tested on \
290                   all supported platforms",
291         issue = "44930"
292     )]
293     pub trait VaArgSafe {}
294 }
295
296 macro_rules! impl_va_arg_safe {
297     ($($t:ty),+) => {
298         $(
299             #[unstable(feature = "c_variadic",
300                        reason = "the `c_variadic` feature has not been properly tested on \
301                                  all supported platforms",
302                        issue = "44930")]
303             impl sealed_trait::VaArgSafe for $t {}
304         )+
305     }
306 }
307
308 impl_va_arg_safe! {i8, i16, i32, i64, usize}
309 impl_va_arg_safe! {u8, u16, u32, u64, isize}
310 impl_va_arg_safe! {f64}
311
312 #[unstable(
313     feature = "c_variadic",
314     reason = "the `c_variadic` feature has not been properly tested on \
315               all supported platforms",
316     issue = "44930"
317 )]
318 impl<T> sealed_trait::VaArgSafe for *mut T {}
319 #[unstable(
320     feature = "c_variadic",
321     reason = "the `c_variadic` feature has not been properly tested on \
322               all supported platforms",
323     issue = "44930"
324 )]
325 impl<T> sealed_trait::VaArgSafe for *const T {}
326
327 #[unstable(
328     feature = "c_variadic",
329     reason = "the `c_variadic` feature has not been properly tested on \
330               all supported platforms",
331     issue = "44930"
332 )]
333 impl<'f> VaListImpl<'f> {
334     /// Advance to the next arg.
335     #[inline]
336     pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
337         // SAFETY: the caller must uphold the safety contract for `va_arg`.
338         unsafe { va_arg(self) }
339     }
340
341     /// Copies the `va_list` at the current location.
342     pub unsafe fn with_copy<F, R>(&self, f: F) -> R
343     where
344         F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
345     {
346         let mut ap = self.clone();
347         let ret = f(ap.as_va_list());
348         // SAFETY: the caller must uphold the safety contract for `va_end`.
349         unsafe {
350             va_end(&mut ap);
351         }
352         ret
353     }
354 }
355
356 #[unstable(
357     feature = "c_variadic",
358     reason = "the `c_variadic` feature has not been properly tested on \
359               all supported platforms",
360     issue = "44930"
361 )]
362 impl<'f> Clone for VaListImpl<'f> {
363     #[inline]
364     fn clone(&self) -> Self {
365         let mut dest = crate::mem::MaybeUninit::uninit();
366         // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
367         unsafe {
368             va_copy(dest.as_mut_ptr(), self);
369             dest.assume_init()
370         }
371     }
372 }
373
374 #[unstable(
375     feature = "c_variadic",
376     reason = "the `c_variadic` feature has not been properly tested on \
377               all supported platforms",
378     issue = "44930"
379 )]
380 impl<'f> Drop for VaListImpl<'f> {
381     fn drop(&mut self) {
382         // FIXME: this should call `va_end`, but there's no clean way to
383         // guarantee that `drop` always gets inlined into its caller,
384         // so the `va_end` would get directly called from the same function as
385         // the corresponding `va_copy`. `man va_end` states that C requires this,
386         // and LLVM basically follows the C semantics, so we need to make sure
387         // that `va_end` is always called from the same function as `va_copy`.
388         // For more details, see https://github.com/rust-lang/rust/pull/59625
389         // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
390         //
391         // This works for now, since `va_end` is a no-op on all current LLVM targets.
392     }
393 }
394
395 extern "rust-intrinsic" {
396     /// Destroy the arglist `ap` after initialization with `va_start` or
397     /// `va_copy`.
398     fn va_end(ap: &mut VaListImpl<'_>);
399
400     /// Copies the current location of arglist `src` to the arglist `dst`.
401     fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
402
403     /// Loads an argument of type `T` from the `va_list` `ap` and increment the
404     /// argument `ap` points to.
405     fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
406 }