Skip to content
Open
Changes from 7 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
16 changes: 13 additions & 3 deletions 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,9 +1841,19 @@ 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.
/// - 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).
/// - 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

/// - It has size 0.
/// - It has alignment 1.
/// - One of the following apply:
/// - It is a `repr(Rust)` (implicitly or explicitly, possibly with additional modifiers such as `packed`) `struct`, `enum`, `union` (regardless of its fields).
/// - It is a [tuple][prim_tuple] (regardless of its fields, and including [`()`][prim_unit]).

@workingjubilee workingjubilee Jul 20, 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.

Less parens, please.

Suggested change
/// - It is a [tuple][prim_tuple] (regardless of its fields, and including [`()`][prim_unit]).
/// - It is a [tuple][prim_tuple], including [`()`][prim_unit], regardless of its fields.

View changes since the review

@workingjubilee workingjubilee Jul 20, 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.

Rephrasing without the specific note about parens: I think reminding people that () is technically considered a tuple here is more important, thus should come first.

I initially considered suggesting just removing every "regardless of..." case and letting the absence of qualifier speak for itself. There are good reasons to not, however.

/// - It is a `repr(transparent)` `struct`, `enum`, or `union`, and all fields have trivial ABI.
/// - It is an array, and its element type has trivial ABI. (This requirement applies even to arrays of length 0.)
Comment thread
workingjubilee marked this conversation as resolved.
/// - It is [the never type `!`][prim_never].

@Darksonn Darksonn Jul 26, 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.

Are we completely sure that we want noreturn functions to be ABI-compatible with functions that are not noreturn?

View changes since the review

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.

People tell me that that's fine, and I don't know enough about what ABIs do here to be able to point at potential problems. Cc @workingjubilee @chorman0773 @CAD97

Note that if we don't want this, we'll need to change more than just remove this last item in the list. For instance, we'd also have to declare (!,) to not have trivial ABI.

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 not aware of any problems with doing it ... there's no separate signature for noreturn in C to begin with. It just seems like a tricky assumption to me.

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.

One could imagine a custom rust ABI that omits the information required to return from the function if we know it anyway doesn't return.

OTOH at least in LLVM the concept of an uninhabited type does not exist and noreturn is just an attribute giving the optimizer more information.

@chorman0773 chorman0773 Jul 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Notably, you can call a _Noreturn/[[noreturn]] function via a pointer to void(void) (if the signature is _Noreturn void foo(void)), as _Noreturn isn't part of the function type in C (its part of the function declaration). Thus, the two kinds of functions must have the same C abi in all relevant cases.
I don't see a reason for us to then say "They might have different abi", when that only applies to Rust-Rust calls.

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.

At worst, if we ever want to do FFI with some hypothetical language whose ABI does make a distinction, we can have a separate -noreturn ABI for them.

/// - It is a function item type or closure type.
Comment on lines +1852 to +1854

@Darksonn Darksonn Jul 15, 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 think this change is reasonable. We have to change these rules because they're just wrong and this is unfixable. The rules as stated seem like a reasonable way to fix it.

My main question would be: Why include arrays? I get a bit worried about that one because array arguments have weird properties in C. We don't want to run into another case in the future where Rust cannot represent certain kinds of C ABIs involving arrays.

View changes since the review

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.

See discussion thread at #157973 (comment). The "and its element type has trivial ABI" requirement should exclude array types with C equivalents.

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.

Hrm, I guess you are right that the element type having a trivial ABI requirement is probably sufficient.

/// - A `repr(transparent)` type is ABI-compatible with its unique field that does not have trivial ABI
/// (as defined above), if such a field exists.
Comment thread
Jules-Bertholet marked this conversation as resolved.
Outdated
/// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.
/// - If `T` is guaranteed to be subject to the [null pointer
/// optimization](option/index.html#representation), and `E` is an enum satisfying the following
Expand Down
Loading