Skip to content

Distinguish repr(C) ZSTs from others in ABI computation#156112

Open
Jules-Bertholet wants to merge 1 commit intorust-lang:mainfrom
Jules-Bertholet:distinguish-c-zsts
Open

Distinguish repr(C) ZSTs from others in ABI computation#156112
Jules-Bertholet wants to merge 1 commit intorust-lang:mainfrom
Jules-Bertholet:distinguish-c-zsts

Conversation

@Jules-Bertholet
Copy link
Copy Markdown
Contributor

@Jules-Bertholet Jules-Bertholet commented May 3, 2026

View all comments

Some C ABIs pass and return ZSTs by pointer. But () should never be returned by pointer, as it must match void. To fix this, distinguish repr(C) ZSTs (and repr(transparent) wrappers around them) from other ZSTs in ABI computation.

Changes documented ABI guarantees (library/core/src/primitive_docs.rs), therefore:
@rustbot label T-lang needs-fcp

Non-Windows ABIs that pass ZSTs indirectly (e.g. Linux powerpc, sparc64, s390x) need more investigation and tests to make sure we always do the right thing. (This PR shouldn't make them any more broken than they already are, but it might not fully fix them.)

Fixes rust-lang/unsafe-code-guidelines#552; see also #78586, #155299.

cc @RalfJung

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 3, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 3, 2026

r? @JohnTitor

rustbot has assigned @JohnTitor.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 22 candidates

@rustbot rustbot added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. T-lang Relevant to the language team labels May 3, 2026
@rust-log-analyzer

This comment has been minimized.

@RalfJung
Copy link
Copy Markdown
Member

RalfJung commented May 3, 2026

Fixes rust-lang/unsafe-code-guidelines#552;

I don't think this fixes that issue. We need #155299 for that (and a wording change to the ABI docs).


This new logic can be entirely removed again once we compute the right layout for zero-element repr(C) structs -- right? So it's more of a temporary work-around than something we "should" be doing?


Let's see if this new field shows up in perf.
@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label May 3, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request May 3, 2026
Distinguish `repr(C)` ZSTs from others in ABI computation
Comment thread tests/codegen-llvm/abi-win64-zst.rs Outdated
// CHECK: define win64cc void @pass_zst_win64(ptr {{[^,]*}})
// CHECK: define win64cc void @pass_rust_zst_win64()
#[no_mangle]
extern "win64" fn pass_rust_zst_win64(_: ()) {}
Copy link
Copy Markdown
Member

@RalfJung RalfJung May 3, 2026

Choose a reason for hiding this comment

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

This will be interesting... I am not sure how to make sense of this ABI on non-MSVC targets. Given that the type layout itself is "wrong", how could one possibly actually call such a function...?

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.

You could call such a function from C with a signature of void pass_rust_zst_win64(). However, the Rust signature trips the improper_ctypes lint, so that's not something we are committing to.

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.

Currently, you could not. We don't allow any arguments to be omitted at the moment.

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.

Also note that I am talking about the "win64" ABI in general, not just this particular function -- I should have been more clear about that.

A "win64" function call on a windows-gnu or Linux target that involves a zero-field struct (or struct whose only field is a 0-element array) might just produce nonsense, right? We'd need repr(win64) as well.

size,
max_repr_align: None,
unadjusted_abi_align: element.align.abi,
repr_c: false,
Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 3, 2026

Choose a reason for hiding this comment

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

This could be:

Suggested change
repr_c: false,
repr_c: element.repr_c,

Or even:

Suggested change
repr_c: false,
repr_c: true,

But I don't want this PR to get bogged down in controversy, so let's leave it alone for now (unless there is lang team consensus).

If we do change this, the documentation in library/core/src/primitive_docs.rs will need to change as well.

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.

It's unclear to me what the consequence of either of the tree possible options here is.

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.

  • false: all ZST arrays have trivial ABI for all element types
  • element.repr_c: ZST arrays are passed like a repr(C) ZST if the element type is repr(C)
  • true: ZST arrays are passed like a repr(C) ZST for all element types

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.

This codepath affects all arrays, not just ZST, right? I guess your point is that for non-ZST repr_c (currently) doesn't make a difference?

ZST arrays are passed like a repr(C) ZST if the element type is repr(C)

That's what I expected based on the description.

... now I can't find it any more, did you change the doc comment for this field? Propagating this through arrays made sense to me at first sight but I did not think deeply about it.

Also, is it correct to say "passed like a repr(C) ZST"? For all I know, C might have different zero-sized types that are passed differently.

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 3, 2026

Choose a reason for hiding this comment

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

did you change the doc comment for this field?

Yes.

Also, is it correct to say "passed like a repr(C) ZST"

"Passed like a repr(C) ZST with the same alignment as the element type".

For all I know, C might have different zero-sized types that are passed differently.

Standard C doesn't have zero-sized types at all. But yes, widespread compiler extensions do make this happen: https://godbolt.org/z/1xWqqj3qv

Currently, Rust can't match the flexible-array-member ABI. This PR leaves that unchanged.

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 4, 2026

Choose a reason for hiding this comment

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

Ah, it looks like flexible array members having a different ABI is just a quirk of Clang, not replicated by GCC: https://godbolt.org/z/eWKazGxxW

Filed an issue with LLVM: llvm/llvm-project#195572

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.

This makes me question again whether we should support any kind of ZST in our C interop, given that those types do not exist in standard C, and C compilers cannot even agree among themselves...

@Jules-Bertholet
Copy link
Copy Markdown
Contributor Author

Jules-Bertholet commented May 3, 2026

and a wording change to the ABI docs

That's part of this PR.

This new logic can be entirely removed again once we compute the right layout for zero-element repr(C) structs -- right?

No, because these structs should remain zero-sized on many targets, including ones where they are passed by pointer.

@rustbot rustbot removed the T-libs Relevant to the library team, which will review and decide on the PR/issue. label May 3, 2026
@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented May 3, 2026

☀️ Try build successful (CI)
Build commit: 167ff94 (167ff940253840b897a826ca8996cc40ce7c71cd, parent: 54f67d248b14af80be8f3adc2094dd4a84ec2115)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (167ff94): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.1% [0.0%, 0.1%] 4
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.1% [-0.6%, -0.0%] 5
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results (primary -3.1%, secondary 2.4%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.9% [2.0%, 5.1%] 3
Improvements ✅
(primary)
-3.1% [-3.1%, -3.1%] 1
Improvements ✅
(secondary)
-2.4% [-2.4%, -2.4%] 1
All ❌✅ (primary) -3.1% [-3.1%, -3.1%] 1

Cycles

Results (secondary -0.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.9% [0.6%, 5.5%] 7
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-2.6% [-4.1%, -0.6%] 6
All ❌✅ (primary) - - 0

Binary size

Results (primary -0.0%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.0% [-0.0%, -0.0%] 4
Improvements ✅
(secondary)
-0.0% [-0.1%, -0.0%] 14
All ❌✅ (primary) -0.0% [-0.0%, -0.0%] 4

Bootstrap: 494.817s -> 493.602s (-0.25%)
Artifact size: 394.41 MiB -> 396.36 MiB (0.49%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels May 3, 2026
@RalfJung
Copy link
Copy Markdown
Member

RalfJung commented May 3, 2026

That's part of this PR.

If you are putting non-trivial implementation changes into the same PR as insta-stable docs updates (that will require t-lang FCP), you're making this PR a lot more difficult to work with. It'd be a good idea to split this up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. perf-regression Performance regression. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Any two types with size 0 and alignment 1 are ABI-compatible" vs the Windows ABI

6 participants