Skip to content

Commit 3ea9eeb

Browse files
authored
Rollup merge of #94503 - joshtriplett:core-ffi-c, r=Amanieu
Provide C FFI types via core::ffi, not just in std Tracking issue: #94501 The ability to interoperate with C code via FFI is not limited to crates using std; this allows using these types without std. The existing types in `std::os::raw` become type aliases for the ones in `core::ffi`. This uses type aliases rather than re-exports, to allow the std types to remain stable while the core types are unstable. This also moves the currently unstable `NonZero_` variants and `c_size_t`/`c_ssize_t`/`c_ptrdiff_t` types to `core::ffi`, while leaving them unstable. Historically, we didn't do this because these types are target-dependent. However, `core` itself is also target-dependent. `core` should not call any OS services, but it knows the target and the target's ABI.
2 parents 1ff654a + 75c3e9c commit 3ea9eeb

23 files changed

+283
-168
lines changed

Diff for: library/std/src/os/raw/char.md renamed to library/core/src/ffi/c_char.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ Equivalent to C's `char` type.
22

33
[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addresses memory with 8-bit bytes.
44

5-
C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See [`CStr`] for more information.
5+
C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See `CStr` for more information.
66

77
[C's `char` type]: https://en.wikipedia.org/wiki/C_data_types#Basic_types
88
[Rust's `char` type]: char
9-
[`CStr`]: crate::ffi::CStr
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: library/core/src/ffi/c_void.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Equivalent to C's `void` type when used as a [pointer].
2+
3+
In essence, `*const c_void` is equivalent to C's `const void*`
4+
and `*mut c_void` is equivalent to C's `void*`. That said, this is
5+
*not* the same as C's `void` return type, which is Rust's `()` type.
6+
7+
To model pointers to opaque types in FFI, until `extern type` is
8+
stabilized, it is recommended to use a newtype wrapper around an empty
9+
byte array. See the [Nomicon] for details.
10+
11+
One could use `std::os::raw::c_void` if they want to support old Rust
12+
compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by
13+
this definition. For more information, please read [RFC 2521].
14+
15+
[Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
16+
[RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md

Diff for: library/core/src/ffi.rs renamed to library/core/src/ffi/mod.rs

+145-15
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,157 @@
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+
19
#![stable(feature = "", since = "1.30.0")]
210
#![allow(non_camel_case_types)]
311

4-
//! Utilities related to foreign function interface (FFI) bindings.
5-
612
use crate::fmt;
713
use crate::marker::PhantomData;
14+
use crate::num::*;
815
use crate::ops::{Deref, DerefMut};
916

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.
17+
macro_rules! type_alias_no_nz {
18+
{
19+
$Docfile:tt, $Alias:ident = $Real:ty;
20+
$( $Cfg:tt )*
21+
} => {
22+
#[doc = include_str!($Docfile)]
23+
$( $Cfg )*
24+
#[unstable(feature = "core_ffi_c", issue = "94501")]
25+
pub type $Alias = $Real;
26+
}
27+
}
28+
29+
// To verify that the NonZero types in this file's macro invocations correspond
30+
//
31+
// perl -n < library/std/src/os/raw/mod.rs -e 'next unless m/type_alias\!/; die "$_ ?" unless m/, (c_\w+) = (\w+), NonZero_(\w+) = NonZero(\w+)/; die "$_ ?" unless $3 eq $1 and $4 eq ucfirst $2'
32+
//
33+
// NB this does not check that the main c_* types are right.
34+
35+
macro_rules! type_alias {
36+
{
37+
$Docfile:tt, $Alias:ident = $Real:ty, $NZAlias:ident = $NZReal:ty;
38+
$( $Cfg:tt )*
39+
} => {
40+
type_alias_no_nz! { $Docfile, $Alias = $Real; $( $Cfg )* }
41+
42+
#[doc = concat!("Type alias for `NonZero` version of [`", stringify!($Alias), "`]")]
43+
#[unstable(feature = "raw_os_nonzero", issue = "82363")]
44+
$( $Cfg )*
45+
pub type $NZAlias = $NZReal;
46+
}
47+
}
48+
49+
type_alias! { "c_char.md", c_char = c_char_definition::c_char, NonZero_c_char = c_char_definition::NonZero_c_char;
50+
// Make this type alias appear cfg-dependent so that Clippy does not suggest
51+
// replacing `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be removed
52+
// after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093
53+
// is fixed.
54+
#[cfg(all())]
55+
#[doc(cfg(all()))] }
56+
type_alias! { "c_schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; }
57+
type_alias! { "c_uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; }
58+
type_alias! { "c_short.md", c_short = i16, NonZero_c_short = NonZeroI16; }
59+
type_alias! { "c_ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; }
60+
type_alias! { "c_int.md", c_int = i32, NonZero_c_int = NonZeroI32; }
61+
type_alias! { "c_uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; }
62+
type_alias! { "c_long.md", c_long = i32, NonZero_c_long = NonZeroI32;
63+
#[doc(cfg(all()))]
64+
#[cfg(any(target_pointer_width = "32", windows))] }
65+
type_alias! { "c_ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32;
66+
#[doc(cfg(all()))]
67+
#[cfg(any(target_pointer_width = "32", windows))] }
68+
type_alias! { "c_long.md", c_long = i64, NonZero_c_long = NonZeroI64;
69+
#[doc(cfg(all()))]
70+
#[cfg(all(target_pointer_width = "64", not(windows)))] }
71+
type_alias! { "c_ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64;
72+
#[doc(cfg(all()))]
73+
#[cfg(all(target_pointer_width = "64", not(windows)))] }
74+
type_alias! { "c_longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; }
75+
type_alias! { "c_ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; }
76+
type_alias_no_nz! { "c_float.md", c_float = f32; }
77+
type_alias_no_nz! { "c_double.md", c_double = f64; }
78+
79+
/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++).
1580
///
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.
81+
/// This type is currently always [`usize`], however in the future there may be
82+
/// platforms where this is not the case.
83+
#[unstable(feature = "c_size_t", issue = "88345")]
84+
pub type c_size_t = usize;
85+
86+
/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++).
1987
///
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].
88+
/// This type is currently always [`isize`], however in the future there may be
89+
/// platforms where this is not the case.
90+
#[unstable(feature = "c_size_t", issue = "88345")]
91+
pub type c_ptrdiff_t = isize;
92+
93+
/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type.
2394
///
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
95+
/// This type is currently always [`isize`], however in the future there may be
96+
/// platforms where this is not the case.
97+
#[unstable(feature = "c_size_t", issue = "88345")]
98+
pub type c_ssize_t = isize;
99+
100+
mod c_char_definition {
101+
cfg_if! {
102+
// These are the targets on which c_char is unsigned.
103+
if #[cfg(any(
104+
all(
105+
target_os = "linux",
106+
any(
107+
target_arch = "aarch64",
108+
target_arch = "arm",
109+
target_arch = "hexagon",
110+
target_arch = "powerpc",
111+
target_arch = "powerpc64",
112+
target_arch = "s390x",
113+
target_arch = "riscv64",
114+
target_arch = "riscv32"
115+
)
116+
),
117+
all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")),
118+
all(target_os = "l4re", target_arch = "x86_64"),
119+
all(
120+
target_os = "freebsd",
121+
any(
122+
target_arch = "aarch64",
123+
target_arch = "arm",
124+
target_arch = "powerpc",
125+
target_arch = "powerpc64",
126+
target_arch = "riscv64"
127+
)
128+
),
129+
all(
130+
target_os = "netbsd",
131+
any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")
132+
),
133+
all(target_os = "openbsd", target_arch = "aarch64"),
134+
all(
135+
target_os = "vxworks",
136+
any(
137+
target_arch = "aarch64",
138+
target_arch = "arm",
139+
target_arch = "powerpc64",
140+
target_arch = "powerpc"
141+
)
142+
),
143+
all(target_os = "fuchsia", target_arch = "aarch64")
144+
))] {
145+
pub type c_char = u8;
146+
pub type NonZero_c_char = crate::num::NonZeroU8;
147+
} else {
148+
// On every other target, c_char is signed.
149+
pub type c_char = i8;
150+
pub type NonZero_c_char = crate::num::NonZeroI8;
151+
}
152+
}
153+
}
154+
26155
// N.B., for LLVM to recognize the void pointer type and by extension
27156
// functions like malloc(), we need to have it represented as i8* in
28157
// LLVM bitcode. The enum used here ensures this and prevents misuse
@@ -31,6 +160,7 @@ use crate::ops::{Deref, DerefMut};
31160
// otherwise and we need at least one variant as otherwise the enum
32161
// would be uninhabited and at least dereferencing such pointers would
33162
// be UB.
163+
#[doc = include_str!("c_void.md")]
34164
#[repr(u8)]
35165
#[stable(feature = "core_c_void", since = "1.30.0")]
36166
pub enum c_void {

Diff for: library/core/src/internal_macros.rs

+93
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,96 @@ macro_rules! impl_fn_for_zst {
187187
)+
188188
}
189189
}
190+
191+
/// A macro for defining `#[cfg]` if-else statements.
192+
///
193+
/// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade
194+
/// of `#[cfg]` cases, emitting the implementation which matches first.
195+
///
196+
/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code without having to
197+
/// rewrite each clause multiple times.
198+
///
199+
/// # Example
200+
///
201+
/// ```
202+
/// cfg_if! {
203+
/// if #[cfg(unix)] {
204+
/// fn foo() { /* unix specific functionality */ }
205+
/// } else if #[cfg(target_pointer_width = "32")] {
206+
/// fn foo() { /* non-unix, 32-bit functionality */ }
207+
/// } else {
208+
/// fn foo() { /* fallback implementation */ }
209+
/// }
210+
/// }
211+
///
212+
/// # fn main() {}
213+
/// ```
214+
// This is a copy of `cfg_if!` from the `cfg_if` crate.
215+
// The recursive invocations should use $crate if this is ever exported.
216+
macro_rules! cfg_if {
217+
// match if/else chains with a final `else`
218+
(
219+
$(
220+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
221+
) else+
222+
else { $( $e_tokens:tt )* }
223+
) => {
224+
cfg_if! {
225+
@__items () ;
226+
$(
227+
(( $i_meta ) ( $( $i_tokens )* )) ,
228+
)+
229+
(() ( $( $e_tokens )* )) ,
230+
}
231+
};
232+
233+
// match if/else chains lacking a final `else`
234+
(
235+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
236+
$(
237+
else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* }
238+
)*
239+
) => {
240+
cfg_if! {
241+
@__items () ;
242+
(( $i_meta ) ( $( $i_tokens )* )) ,
243+
$(
244+
(( $e_meta ) ( $( $e_tokens )* )) ,
245+
)*
246+
}
247+
};
248+
249+
// Internal and recursive macro to emit all the items
250+
//
251+
// Collects all the previous cfgs in a list at the beginning, so they can be
252+
// negated. After the semicolon is all the remaining items.
253+
(@__items ( $( $_:meta , )* ) ; ) => {};
254+
(
255+
@__items ( $( $no:meta , )* ) ;
256+
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
257+
$( $rest:tt , )*
258+
) => {
259+
// Emit all items within one block, applying an appropriate #[cfg]. The
260+
// #[cfg] will require all `$yes` matchers specified and must also negate
261+
// all previous matchers.
262+
#[cfg(all(
263+
$( $yes , )?
264+
not(any( $( $no ),* ))
265+
))]
266+
cfg_if! { @__identity $( $tokens )* }
267+
268+
// Recurse to emit all other items in `$rest`, and when we do so add all
269+
// our `$yes` matchers to the list of `$no` matchers as future emissions
270+
// will have to negate everything we just matched as well.
271+
cfg_if! {
272+
@__items ( $( $no , )* $( $yes , )? ) ;
273+
$( $rest , )*
274+
}
275+
};
276+
277+
// Internal macro to make __apply work out right for different match types,
278+
// because of how macros match/expand stuff.
279+
(@__identity $( $tokens:tt )* ) => {
280+
$( $tokens )*
281+
};
282+
}

Diff for: library/std/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@
261261
#![feature(const_socketaddr)]
262262
#![feature(const_trait_impl)]
263263
#![feature(container_error_extra)]
264+
#![feature(core_ffi_c)]
264265
#![feature(core_intrinsics)]
265266
#![feature(core_panic)]
266267
#![feature(custom_test_frameworks)]
@@ -315,6 +316,7 @@
315316
#![feature(prelude_import)]
316317
#![feature(ptr_as_uninit)]
317318
#![feature(ptr_internals)]
319+
#![feature(raw_os_nonzero)]
318320
#![feature(rustc_attrs)]
319321
#![feature(rustc_private)]
320322
#![feature(saturating_int_impl)]

0 commit comments

Comments
 (0)