Skip to content

Commit 1cabedc

Browse files
authored
Rollup merge of #115476 - RalfJung:abi-compat-docs, r=Mark-Simulacrum
document ABI compatibility I don't think we have any central place where we document our ABI compatibility rules, so let's create one. The `fn()` pointer type seems like a good place since ABI questions can only become relevant when invoking a function through a function pointer. This will likely need T-lang FCP.
2 parents 4770d91 + 8f03a55 commit 1cabedc

File tree

3 files changed

+117
-8
lines changed

3 files changed

+117
-8
lines changed

library/core/src/option.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -119,27 +119,28 @@
119119
//! # Representation
120120
//!
121121
//! Rust guarantees to optimize the following types `T` such that
122-
//! [`Option<T>`] has the same size and alignment as `T`. In some
122+
//! [`Option<T>`] has the same size, alignment, and [function call ABI] as `T`. In some
123123
//! of these cases, Rust further guarantees that
124124
//! `transmute::<_, Option<T>>([0u8; size_of::<T>()])` is sound and
125125
//! produces `Option::<T>::None`. These cases are identified by the
126126
//! second column:
127127
//!
128128
//! | `T` | `transmute::<_, Option<T>>([0u8; size_of::<T>()])` sound? |
129129
//! |---------------------------------------------------------------------|----------------------------------------------------------------------|
130-
//! | [`Box<U>`] | when `U: Sized` |
130+
//! | [`Box<U>`] (specifically, only `Box<U, Global>`) | when `U: Sized` |
131131
//! | `&U` | when `U: Sized` |
132132
//! | `&mut U` | when `U: Sized` |
133133
//! | `fn`, `extern "C" fn`[^extern_fn] | always |
134134
//! | [`num::NonZero*`] | always |
135135
//! | [`ptr::NonNull<U>`] | when `U: Sized` |
136136
//! | `#[repr(transparent)]` struct around one of the types in this list. | when it holds for the inner type |
137137
//!
138-
//! [^extern_fn]: this remains true for any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`)
138+
//! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`)
139139
//!
140140
//! [`Box<U>`]: ../../std/boxed/struct.Box.html
141141
//! [`num::NonZero*`]: crate::num
142142
//! [`ptr::NonNull<U>`]: crate::ptr::NonNull
143+
//! [function call ABI]: ../primitive.fn.html#abi-compatibility
143144
//!
144145
//! This is called the "null pointer optimization" or NPO.
145146
//!

library/core/src/primitive_docs.rs

+109-1
Original file line numberDiff line numberDiff line change
@@ -1493,7 +1493,7 @@ mod prim_ref {}
14931493
///
14941494
/// ### Casting to and from integers
14951495
///
1496-
/// You cast function pointers directly to integers:
1496+
/// You can cast function pointers directly to integers:
14971497
///
14981498
/// ```rust
14991499
/// let fnptr: fn(i32) -> i32 = |x| x+2;
@@ -1519,6 +1519,114 @@ mod prim_ref {}
15191519
/// Note that all of this is not portable to platforms where function pointers and data pointers
15201520
/// have different sizes.
15211521
///
1522+
/// ### ABI compatibility
1523+
///
1524+
/// Generally, when a function is declared with one signature and called via a function pointer with
1525+
/// a different signature, the two signatures must be *ABI-compatible* or else calling the function
1526+
/// via that function pointer is Undefined Behavior. ABI compatibility is a lot stricter than merely
1527+
/// having the same memory layout; for example, even if `i32` and `f32` have the same size and
1528+
/// alignment, they might be passed in different registers and hence not be ABI-compatible.
1529+
///
1530+
/// ABI compatibility as a concern only arises in code that alters the type of function pointers,
1531+
/// code that imports functions via `extern` blocks, and in code that combines `#[target_feature]`
1532+
/// with `extern fn`. Altering the type of function pointers is wildly unsafe (as in, a lot more
1533+
/// unsafe than even [`transmute_copy`][mem::transmute_copy]), and should only occur in the most
1534+
/// exceptional circumstances. Most Rust code just imports functions via `use`. `#[target_feature]`
1535+
/// is also used rarely. So, most likely you do not have to worry about ABI compatibility.
1536+
///
1537+
/// But assuming such circumstances, what are the rules? For this section, we are only considering
1538+
/// the ABI of direct Rust-to-Rust calls, not linking in general -- once functions are imported via
1539+
/// `extern` blocks, there are more things to consider that we do not go into here.
1540+
///
1541+
/// For two signatures to be considered *ABI-compatible*, they must use a compatible ABI string,
1542+
/// must take the same number of arguments, the individual argument types and the return types must
1543+
/// be ABI-compatible, and the target feature requirements must be met (see the subsection below for
1544+
/// the last point). The ABI string is declared via `extern "ABI" fn(...) -> ...`; note that
1545+
/// `fn name(...) -> ...` implicitly uses the `"Rust"` ABI string and `extern fn name(...) -> ...`
1546+
/// implicitly uses the `"C"` ABI string.
1547+
///
1548+
/// The ABI strings are guaranteed to be compatible if they are the same, or if the caller ABI
1549+
/// string is `$X-unwind` and the callee ABI string is `$X`, where `$X` is one of the following:
1550+
/// "C", "aapcs", "fastcall", "stdcall", "system", "sysv64", "thiscall", "vectorcall", "win64".
1551+
///
1552+
/// The following types are guaranteed to be ABI-compatible:
1553+
///
1554+
/// - `*const T`, `*mut T`, `&T`, `&mut T`, `Box<T>` (specifically, only `Box<T, Global>`), and
1555+
/// `NonNull<T>` are all ABI-compatible with each other for all `T`. They are also ABI-compatible
1556+
/// with each other for _different_ `T` if they have the same metadata type (`<T as
1557+
/// Pointee>::Metadata`).
1558+
/// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is
1559+
/// ABI-compatible with the `iN` integer type of the same size.
1560+
/// - Any two `fn` (function pointer) types are ABI-compatible with each other if they have the same
1561+
/// ABI string or the ABI string only differs in a trailing `-unwind`, independent of the rest of
1562+
/// their signature. (This means you can pass `fn()` to a function expecting `fn(i32)`, and the
1563+
/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer
1564+
/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just
1565+
/// almost certainly UB to later call that function pointer.)
1566+
/// - Any two types with size 0 and alignment 1 are ABI-compatible.
1567+
/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the
1568+
/// unique field that doesn't have size 0 and alignment 1 (if there is such a field).
1569+
/// - `i32` is ABI-compatible with `NonZeroI32`, and similar for all other integer types with their
1570+
/// matching `NonZero*` type.
1571+
/// - If `T` is guaranteed to be subject to the [null pointer
1572+
/// optimization](option/index.html#representation), then `T` and `Option<T>` are ABI-compatible.
1573+
///
1574+
/// Furthermore, ABI compatibility satisfies the following general properties:
1575+
///
1576+
/// - Every type is ABI-compatible with itself.
1577+
/// - If `T1` and `T2` are ABI-compatible, then two `repr(C)` types that only differ because one
1578+
/// field type was changed from `T1` to `T2` are ABI-compatible.
1579+
/// - If `T1` and `T2` are ABI-compatible and `T2` and `T3` are ABI-compatible, then so are `T1` and
1580+
/// `T3` (i.e., ABI-compatibility is transitive).
1581+
/// - If `T1` and `T2` are ABI-compatible, then so are `T2` and `T1` (i.e., ABI-compatibility is
1582+
/// symmetric).
1583+
///
1584+
/// More signatures can be ABI-compatible on specific targets, but that should not be relied upon
1585+
/// since it is not portable and not a stable guarantee.
1586+
///
1587+
/// Noteworthy cases of types *not* being ABI-compatible in general are:
1588+
/// * `bool` vs `u8`, and `i32` vs `u32`: on some targets, the calling conventions for these types
1589+
/// differ in terms of what they guarantee for the remaining bits in the register that are not
1590+
/// used by the value.
1591+
/// * `i32` vs `f32` are not compatible either, as has already been mentioned above.
1592+
/// * `struct Foo(u32)` and `u32` are not compatible (without `repr(transparent)`) since structs are
1593+
/// aggregate types and often passed in a different way than primitives like `i32`.
1594+
///
1595+
/// Note that these rules describe when two completely known types are ABI-compatible. When
1596+
/// considering ABI compatibility of a type declared in another crate (including the standard
1597+
/// library), consider that any type that has a private field or the `#[non_exhaustive]` attribute
1598+
/// may change its layout as a non-breaking update unless documented otherwise -- so for instance,
1599+
/// even if such a type is a 1-ZST or `repr(transparent)` right now, this might change with any
1600+
/// library version bump.
1601+
///
1602+
/// If the declared signature and the signature of the function pointer are ABI-compatible, then the
1603+
/// function call behaves as if every argument was [`transmute`d][mem::transmute] from the
1604+
/// type in the function pointer to the type at the function declaration, and the return value is
1605+
/// [`transmute`d][mem::transmute] from the type in the declaration to the type in the
1606+
/// pointer. All the usual caveats and concerns around transmutation apply; for instance, if the
1607+
/// function expects a `NonNullI32` and the function pointer uses the ABI-compatible type
1608+
/// `Option<NonNullI32>`, and the value used for the argument is `None`, then this call is Undefined
1609+
/// Behavior since transmuting `None::<NonNullI32>` to `NonNullI32` violates the non-null
1610+
/// requirement.
1611+
///
1612+
/// #### Requirements concerning target features
1613+
///
1614+
/// Under some conditions, the signature used by the caller and the callee can be ABI-incompatible
1615+
/// even if the exact same ABI string and types are being used. As an example, the
1616+
/// `std::arch::x86_64::__m256` type has a different `extern "C"` ABI when the `avx` feature is
1617+
/// enabled vs when it is not enabled.
1618+
///
1619+
/// Therefore, to ensure ABI compatibility when code using different target features is combined
1620+
/// (such as via `#[target_feature]`), we further require that one of the following conditions is
1621+
/// met:
1622+
///
1623+
/// - The function uses the `"Rust"` ABI string (which is the default without `extern`).
1624+
/// - Caller and callee are using the exact same set of target features. For the callee we consider
1625+
/// the features enabled (via `#[target_feature]` and `-C target-feature`/`-C target-cpu`) at the
1626+
/// declaration site; for the caller we consider the features enabled at the call site.
1627+
/// - Neither any argument nor the return value involves a SIMD type (`#[repr(simd)]`) that is not
1628+
/// behind a pointer indirection (i.e., `*mut __m256` is fine, but `(i32, __m256)` is not).
1629+
///
15221630
/// ### Trait implementations
15231631
///
15241632
/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic

tests/ui/abi/compatibility.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,7 @@ macro_rules! test_abi_compatible {
231231
};
232232
}
233233

234-
// Compatibility of pointers is probably de-facto guaranteed,
235-
// but that does not seem to be documented.
234+
// Compatibility of pointers.
236235
test_abi_compatible!(ptr_mut, *const i32, *mut i32);
237236
test_abi_compatible!(ptr_pointee, *const i32, *const Vec<i32>);
238237
test_abi_compatible!(ref_mut, &i32, &mut i32);
@@ -241,14 +240,15 @@ test_abi_compatible!(box_ptr, Box<i32>, *const i32);
241240
test_abi_compatible!(nonnull_ptr, NonNull<i32>, *const i32);
242241
test_abi_compatible!(fn_fn, fn(), fn(i32) -> i32);
243242

244-
// Some further guarantees we will likely (have to) make.
243+
// Compatibility of 1-ZST.
245244
test_abi_compatible!(zst_unit, Zst, ());
246245
#[cfg(not(any(target_arch = "sparc64")))]
247246
test_abi_compatible!(zst_array, Zst, [u8; 0]);
248247
test_abi_compatible!(nonzero_int, NonZeroI32, i32);
249248

250249
// `DispatchFromDyn` relies on ABI compatibility.
251-
// This is interesting since these types are not `repr(transparent)`.
250+
// This is interesting since these types are not `repr(transparent)`. So this is not part of our
251+
// public ABI guarantees, but is relied on by the compiler.
252252
test_abi_compatible!(rc, Rc<i32>, *mut i32);
253253
test_abi_compatible!(arc, Arc<i32>, *mut i32);
254254

0 commit comments

Comments
 (0)