Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion library/core/src/primitive_docs.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some C ABIs pass and return ZSTs by pointer. But () should never be returned by pointer, as it must match void. To account for this, we have to weaken the present guarantee of "any two types with size 0 and alignment 1 are ABI-compatible" to exclude repr(C).

I think the implication here is that () is repr(C)? Am I reading that right? Where do we make that guarantee? Or is the thinking that the language here would make () and #[repr(C)] struct Foo; not ABI compatible?

One callout is that ZSTs aren't (I think?) standardized -- C and C++ without extensions both require types to be non-ZST if I remember right (e.g., see https://stackoverflow.com/a/2632075). Maybe that has changed since then though?

It seems like at minimum, it would be nice to avoid weakening this guarantee for Rust ABI even if we do so for C ABIs as a result of the weird platforms.

@Jules-Bertholet Jules-Bertholet Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is the thinking that the language here would make () and #[repr(C)] struct Foo; not ABI compatible?

Yes, this.

One callout is that ZSTs aren't (I think?) standardized

Correct.

Original file line number Diff line number Diff line change
Expand Up @@ -1841,7 +1841,15 @@ mod prim_ref {}
/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer
/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just
/// almost certainly UB to later call that function pointer.)
/// - Any two types with size 0 and alignment 1 are ABI-compatible.
/// - Any two types fulfilling all the following conditions are ABI-compatible;
/// such types are said to have "trivial ABI":
Comment on lines +1844 to +1845

@RalfJung RalfJung Jul 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// - Any two types fulfilling all the following conditions are ABI-compatible;
/// such types are said to have "trivial ABI":
/// - Any two types with "trivial ABI" are ABI-compatible.
/// A type has trivial ABI if is satisfies all of the following:

View changes since the review

/// - Size 0
/// - Alignment 1
/// - Not `repr(C)`
/// - Not a `repr(transparent)` wrapper around a type that fails to satisfy these conditions
Comment thread
RalfJung marked this conversation as resolved.
Outdated
/// - Not an array whose element type fails to satisfy these conditions

@Jules-Bertholet Jules-Bertholet Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We exempt arrays for 2 reasons:

  • A future version of C, or some other language we would like to do FFI with, might allow passing arrays to functions directly by value, in a way that would conflict with these guarantees
  • It would be nice to also use "trivial ABI" in the specification of repr(C), and we need this clause for that. See discussion at repr(ordered_fields) rfcs#3845 (comment)

We could also simplify this clause by saying merely:

Suggested change
/// - Not an array whose element type fails to satisfy these conditions
/// - Not an array

The downside would be a larger breaking change.

Note that repr(transparent) will need to be adjusted to account for this change (by rejecting non-trivial arrays as "additional" fields).

View changes since the review

@RalfJung RalfJung Jul 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with saying that "all arrays with trivial-ABI element type have trivial ABI".

However, even that less-breaking version breaks ghost again, right? Similar to dtolnay/ghost#41. ghost uses a zero-length array of *const T, which definitely does not have trivial ABI. I don't know why they do that...

EDIT: Ah, ghost is saved by having a repr(Rust) type around the array, under the rules discussed here.

/// - A `repr(transparent)` type is ABI-compatible with its unique field that does not have trivial ABI
/// (as defined above). If there is no such field, the type has trivial ABI.
/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the
/// unique field that doesn't have size 0 and alignment 1 (if there is such a field).
Comment thread
Jules-Bertholet marked this conversation as resolved.
Outdated
/// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.
Expand Down
Loading