1//! Platform-specific types, as defined by C.
2//!
3//! Code that interacts via FFI will almost certainly be using the
4//! base types provided by C, which aren't nearly as nicely defined
5//! as Rust's primitive types. This module provides types which will
6//! match those defined by C, so that code that interacts with C will
7//! refer to the correct types.
8
9#![stable(feature = "core_ffi", since = "1.30.0")]
10#![allow(non_camel_case_types)]
11
12use crate::fmt;
13use crate::marker::PhantomData;
14use crate::ops::{Deref, DerefMut};
15
16#[stable(feature = "core_c_str", since = "1.64.0")]
17pub use self::c_str::{CStr, FromBytesUntilNulError, FromBytesWithNulError};
18
19mod c_str;
20
21macro_rules! type_alias {
22 {
23 $Docfile:tt, $Alias:ident = $Real:ty;
24 $( $Cfg:tt )*
25 } => {
26 #[doc = include_str!($Docfile)]
27 $( $Cfg )*
28 #[stable(feature = "core_ffi_c", since = "1.64.0")]
29 pub type $Alias = $Real;
30 }
31}
32
33type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] }
34
35type_alias! { "c_schar.md", c_schar = i8; }
36type_alias! { "c_uchar.md", c_uchar = u8; }
37type_alias! { "c_short.md", c_short = i16; }
38type_alias! { "c_ushort.md", c_ushort = u16; }
39
40type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] }
41type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] }
42
43type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] }
44type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] }
45
46type_alias! { "c_longlong.md", c_longlong = i64; }
47type_alias! { "c_ulonglong.md", c_ulonglong = u64; }
48
49type_alias! { "c_float.md", c_float = f32; }
50type_alias! { "c_double.md", c_double = f64; }
51
52/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++).
53///
54/// This type is currently always [`usize`], however in the future there may be
55/// platforms where this is not the case.
56#[unstable(feature = "c_size_t", issue = "88345")]
57pub type c_size_t = usize;
58
59/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++).
60///
61/// This type is currently always [`isize`], however in the future there may be
62/// platforms where this is not the case.
63#[unstable(feature = "c_size_t", issue = "88345")]
64pub type c_ptrdiff_t = isize;
65
66/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type.
67///
68/// This type is currently always [`isize`], however in the future there may be
69/// platforms where this is not the case.
70#[unstable(feature = "c_size_t", issue = "88345")]
71pub type c_ssize_t = isize;
72
73mod c_char_definition {
74 cfg_if! {
75 // These are the targets on which c_char is unsigned.
76 if #[cfg(any(
77 all(
78 target_os = "linux",
79 any(
80 target_arch = "aarch64",
81 target_arch = "arm",
82 target_arch = "hexagon",
83 target_arch = "powerpc",
84 target_arch = "powerpc64",
85 target_arch = "s390x",
86 target_arch = "riscv64",
87 target_arch = "riscv32",
88 target_arch = "csky"
89 )
90 ),
91 all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
92 all(target_os = "l4re", target_arch = "x86_64"),
93 all(
94 any(target_os = "freebsd", target_os = "openbsd"),
95 any(
96 target_arch = "aarch64",
97 target_arch = "arm",
98 target_arch = "powerpc",
99 target_arch = "powerpc64",
100 target_arch = "riscv64"
101 )
102 ),
103 all(
104 target_os = "netbsd",
105 any(
106 target_arch = "aarch64",
107 target_arch = "arm",
108 target_arch = "powerpc",
109 target_arch = "riscv64"
110 )
111 ),
112 all(
113 target_os = "vxworks",
114 any(
115 target_arch = "aarch64",
116 target_arch = "arm",
117 target_arch = "powerpc64",
118 target_arch = "powerpc"
119 )
120 ),
121 all(
122 target_os = "fuchsia",
123 any(target_arch = "aarch64", target_arch = "riscv64")
124 ),
125 all(target_os = "nto", target_arch = "aarch64"),
126 target_os = "horizon"
127 ))] {
128 pub type c_char = u8;
129 } else {
130 // On every other target, c_char is signed.
131 pub type c_char = i8;
132 }
133 }
134}
135
136mod c_int_definition {
137 cfg_if! {
138 if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] {
139 pub type c_int = i16;
140 pub type c_uint = u16;
141 } else {
142 pub type c_int = i32;
143 pub type c_uint = u32;
144 }
145 }
146}
147
148mod c_long_definition {
149 cfg_if! {
150 if #[cfg(all(target_pointer_width = "64", not(windows)))] {
151 pub type c_long = i64;
152 pub type c_ulong = u64;
153 } else {
154 // The minimal size of `long` in the C standard is 32 bits
155 pub type c_long = i32;
156 pub type c_ulong = u32;
157 }
158 }
159}
160
161// N.B., for LLVM to recognize the void pointer type and by extension
162// functions like malloc(), we need to have it represented as i8* in
163// LLVM bitcode. The enum used here ensures this and prevents misuse
164// of the "raw" type by only having private variants. We need two
165// variants, because the compiler complains about the repr attribute
166// otherwise and we need at least one variant as otherwise the enum
167// would be uninhabited and at least dereferencing such pointers would
168// be UB.
169#[doc = include_str!("c_void.md")]
170#[lang = "c_void"]
171#[cfg_attr(not(doc), repr(u8))] // work around https://github.com/rust-lang/rust/issues/90435
172#[stable(feature = "core_c_void", since = "1.30.0")]
173pub enum c_void {
174 #[unstable(
175 feature = "c_void_variant",
176 reason = "temporary implementation detail",
177 issue = "none"
178 )]
179 #[doc(hidden)]
180 __variant1,
181 #[unstable(
182 feature = "c_void_variant",
183 reason = "temporary implementation detail",
184 issue = "none"
185 )]
186 #[doc(hidden)]
187 __variant2,
188}
189
190#[stable(feature = "std_debug", since = "1.16.0")]
191impl fmt::Debug for c_void {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 f.debug_struct(name:"c_void").finish()
194 }
195}
196
197/// Basic implementation of a `va_list`.
198// The name is WIP, using `VaListImpl` for now.
199#[cfg(any(
200 all(
201 not(target_arch = "aarch64"),
202 not(target_arch = "powerpc"),
203 not(target_arch = "s390x"),
204 not(target_arch = "x86_64")
205 ),
206 all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
207 target_family = "wasm",
208 target_os = "uefi",
209 windows,
210))]
211#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435
212#[unstable(
213 feature = "c_variadic",
214 reason = "the `c_variadic` feature has not been properly tested on \
215 all supported platforms",
216 issue = "44930"
217)]
218#[lang = "va_list"]
219pub struct VaListImpl<'f> {
220 ptr: *mut c_void,
221
222 // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
223 // the region of the function it's defined in
224 _marker: PhantomData<&'f mut &'f c_void>,
225}
226
227#[cfg(any(
228 all(
229 not(target_arch = "aarch64"),
230 not(target_arch = "powerpc"),
231 not(target_arch = "s390x"),
232 not(target_arch = "x86_64")
233 ),
234 all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
235 target_family = "wasm",
236 target_os = "uefi",
237 windows,
238))]
239#[unstable(
240 feature = "c_variadic",
241 reason = "the `c_variadic` feature has not been properly tested on \
242 all supported platforms",
243 issue = "44930"
244)]
245impl<'f> fmt::Debug for VaListImpl<'f> {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 write!(f, "va_list* {:p}", self.ptr)
248 }
249}
250
251/// AArch64 ABI implementation of a `va_list`. See the
252/// [AArch64 Procedure Call Standard] for more details.
253///
254/// [AArch64 Procedure Call Standard]:
255/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
256#[cfg(all(
257 target_arch = "aarch64",
258 not(any(target_os = "macos", target_os = "ios", target_os = "tvos")),
259 not(target_os = "uefi"),
260 not(windows),
261))]
262#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
263#[derive(Debug)]
264#[unstable(
265 feature = "c_variadic",
266 reason = "the `c_variadic` feature has not been properly tested on \
267 all supported platforms",
268 issue = "44930"
269)]
270#[lang = "va_list"]
271pub struct VaListImpl<'f> {
272 stack: *mut c_void,
273 gr_top: *mut c_void,
274 vr_top: *mut c_void,
275 gr_offs: i32,
276 vr_offs: i32,
277 _marker: PhantomData<&'f mut &'f c_void>,
278}
279
280/// PowerPC ABI implementation of a `va_list`.
281#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))]
282#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
283#[derive(Debug)]
284#[unstable(
285 feature = "c_variadic",
286 reason = "the `c_variadic` feature has not been properly tested on \
287 all supported platforms",
288 issue = "44930"
289)]
290#[lang = "va_list"]
291pub struct VaListImpl<'f> {
292 gpr: u8,
293 fpr: u8,
294 reserved: u16,
295 overflow_arg_area: *mut c_void,
296 reg_save_area: *mut c_void,
297 _marker: PhantomData<&'f mut &'f c_void>,
298}
299
300/// s390x ABI implementation of a `va_list`.
301#[cfg(target_arch = "s390x")]
302#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
303#[derive(Debug)]
304#[unstable(
305 feature = "c_variadic",
306 reason = "the `c_variadic` feature has not been properly tested on \
307 all supported platforms",
308 issue = "44930"
309)]
310#[lang = "va_list"]
311pub struct VaListImpl<'f> {
312 gpr: i64,
313 fpr: i64,
314 overflow_arg_area: *mut c_void,
315 reg_save_area: *mut c_void,
316 _marker: PhantomData<&'f mut &'f c_void>,
317}
318
319/// x86_64 ABI implementation of a `va_list`.
320#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))]
321#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401
322#[derive(Debug)]
323#[unstable(
324 feature = "c_variadic",
325 reason = "the `c_variadic` feature has not been properly tested on \
326 all supported platforms",
327 issue = "44930"
328)]
329#[lang = "va_list"]
330pub struct VaListImpl<'f> {
331 gp_offset: i32,
332 fp_offset: i32,
333 overflow_arg_area: *mut c_void,
334 reg_save_area: *mut c_void,
335 _marker: PhantomData<&'f mut &'f c_void>,
336}
337
338/// A wrapper for a `va_list`
339#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435
340#[derive(Debug)]
341#[unstable(
342 feature = "c_variadic",
343 reason = "the `c_variadic` feature has not been properly tested on \
344 all supported platforms",
345 issue = "44930"
346)]
347pub struct VaList<'a, 'f: 'a> {
348 #[cfg(any(
349 all(
350 not(target_arch = "aarch64"),
351 not(target_arch = "powerpc"),
352 not(target_arch = "s390x"),
353 not(target_arch = "x86_64")
354 ),
355 all(
356 target_arch = "aarch64",
357 any(target_os = "macos", target_os = "ios", target_os = "tvos")
358 ),
359 target_family = "wasm",
360 target_os = "uefi",
361 windows,
362 ))]
363 inner: VaListImpl<'f>,
364
365 #[cfg(all(
366 any(
367 target_arch = "aarch64",
368 target_arch = "powerpc",
369 target_arch = "s390x",
370 target_arch = "x86_64"
371 ),
372 any(
373 not(target_arch = "aarch64"),
374 not(any(target_os = "macos", target_os = "ios", target_os = "tvos"))
375 ),
376 not(target_family = "wasm"),
377 not(target_os = "uefi"),
378 not(windows),
379 ))]
380 inner: &'a mut VaListImpl<'f>,
381
382 _marker: PhantomData<&'a mut VaListImpl<'f>>,
383}
384
385#[cfg(any(
386 all(
387 not(target_arch = "aarch64"),
388 not(target_arch = "powerpc"),
389 not(target_arch = "s390x"),
390 not(target_arch = "x86_64")
391 ),
392 all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")),
393 target_family = "wasm",
394 target_os = "uefi",
395 windows,
396))]
397#[unstable(
398 feature = "c_variadic",
399 reason = "the `c_variadic` feature has not been properly tested on \
400 all supported platforms",
401 issue = "44930"
402)]
403impl<'f> VaListImpl<'f> {
404 /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
405 #[inline]
406 pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
407 VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
408 }
409}
410
411#[cfg(all(
412 any(
413 target_arch = "aarch64",
414 target_arch = "powerpc",
415 target_arch = "s390x",
416 target_arch = "x86_64"
417 ),
418 any(
419 not(target_arch = "aarch64"),
420 not(any(target_os = "macos", target_os = "ios", target_os = "tvos"))
421 ),
422 not(target_family = "wasm"),
423 not(target_os = "uefi"),
424 not(windows),
425))]
426#[unstable(
427 feature = "c_variadic",
428 reason = "the `c_variadic` feature has not been properly tested on \
429 all supported platforms",
430 issue = "44930"
431)]
432impl<'f> VaListImpl<'f> {
433 /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
434 #[inline]
435 pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
436 VaList { inner: self, _marker: PhantomData }
437 }
438}
439
440#[unstable(
441 feature = "c_variadic",
442 reason = "the `c_variadic` feature has not been properly tested on \
443 all supported platforms",
444 issue = "44930"
445)]
446impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
447 type Target = VaListImpl<'f>;
448
449 #[inline]
450 fn deref(&self) -> &VaListImpl<'f> {
451 &self.inner
452 }
453}
454
455#[unstable(
456 feature = "c_variadic",
457 reason = "the `c_variadic` feature has not been properly tested on \
458 all supported platforms",
459 issue = "44930"
460)]
461impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
462 #[inline]
463 fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
464 &mut self.inner
465 }
466}
467
468// The VaArgSafe trait needs to be used in public interfaces, however, the trait
469// itself must not be allowed to be used outside this module. Allowing users to
470// implement the trait for a new type (thereby allowing the va_arg intrinsic to
471// be used on a new type) is likely to cause undefined behavior.
472//
473// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
474// but also ensure it cannot be used elsewhere, the trait needs to be public
475// within a private module. Once RFC 2145 has been implemented look into
476// improving this.
477mod sealed_trait {
478 /// Trait which permits the allowed types to be used with [super::VaListImpl::arg].
479 #[unstable(
480 feature = "c_variadic",
481 reason = "the `c_variadic` feature has not been properly tested on \
482 all supported platforms",
483 issue = "44930"
484 )]
485 pub trait VaArgSafe {}
486}
487
488macro_rules! impl_va_arg_safe {
489 ($($t:ty),+) => {
490 $(
491 #[unstable(feature = "c_variadic",
492 reason = "the `c_variadic` feature has not been properly tested on \
493 all supported platforms",
494 issue = "44930")]
495 impl sealed_trait::VaArgSafe for $t {}
496 )+
497 }
498}
499
500impl_va_arg_safe! {i8, i16, i32, i64, usize}
501impl_va_arg_safe! {u8, u16, u32, u64, isize}
502impl_va_arg_safe! {f64}
503
504#[unstable(
505 feature = "c_variadic",
506 reason = "the `c_variadic` feature has not been properly tested on \
507 all supported platforms",
508 issue = "44930"
509)]
510impl<T> sealed_trait::VaArgSafe for *mut T {}
511#[unstable(
512 feature = "c_variadic",
513 reason = "the `c_variadic` feature has not been properly tested on \
514 all supported platforms",
515 issue = "44930"
516)]
517impl<T> sealed_trait::VaArgSafe for *const T {}
518
519#[unstable(
520 feature = "c_variadic",
521 reason = "the `c_variadic` feature has not been properly tested on \
522 all supported platforms",
523 issue = "44930"
524)]
525impl<'f> VaListImpl<'f> {
526 /// Advance to the next arg.
527 #[inline]
528 pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
529 // SAFETY: the caller must uphold the safety contract for `va_arg`.
530 unsafe { va_arg(self) }
531 }
532
533 /// Copies the `va_list` at the current location.
534 pub unsafe fn with_copy<F, R>(&self, f: F) -> R
535 where
536 F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
537 {
538 let mut ap: VaListImpl<'_> = self.clone();
539 let ret: R = f(ap.as_va_list());
540 // SAFETY: the caller must uphold the safety contract for `va_end`.
541 unsafe {
542 va_end(&mut ap);
543 }
544 ret
545 }
546}
547
548#[unstable(
549 feature = "c_variadic",
550 reason = "the `c_variadic` feature has not been properly tested on \
551 all supported platforms",
552 issue = "44930"
553)]
554impl<'f> Clone for VaListImpl<'f> {
555 #[inline]
556 fn clone(&self) -> Self {
557 let mut dest: MaybeUninit> = crate::mem::MaybeUninit::uninit();
558 // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
559 unsafe {
560 va_copy(dest:dest.as_mut_ptr(), self);
561 dest.assume_init()
562 }
563 }
564}
565
566#[unstable(
567 feature = "c_variadic",
568 reason = "the `c_variadic` feature has not been properly tested on \
569 all supported platforms",
570 issue = "44930"
571)]
572impl<'f> Drop for VaListImpl<'f> {
573 fn drop(&mut self) {
574 // FIXME: this should call `va_end`, but there's no clean way to
575 // guarantee that `drop` always gets inlined into its caller,
576 // so the `va_end` would get directly called from the same function as
577 // the corresponding `va_copy`. `man va_end` states that C requires this,
578 // and LLVM basically follows the C semantics, so we need to make sure
579 // that `va_end` is always called from the same function as `va_copy`.
580 // For more details, see https://github.com/rust-lang/rust/pull/59625
581 // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
582 //
583 // This works for now, since `va_end` is a no-op on all current LLVM targets.
584 }
585}
586
587extern "rust-intrinsic" {
588 /// Destroy the arglist `ap` after initialization with `va_start` or
589 /// `va_copy`.
590 #[rustc_nounwind]
591 fn va_end(ap: &mut VaListImpl<'_>);
592
593 /// Copies the current location of arglist `src` to the arglist `dst`.
594 #[rustc_nounwind]
595 fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
596
597 /// Loads an argument of type `T` from the `va_list` `ap` and increment the
598 /// argument `ap` points to.
599 #[rustc_nounwind]
600 fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
601}
602