Skip to content

Redefine c_void#159935

Open
Jules-Bertholet wants to merge 2 commits into
rust-lang:mainfrom
Jules-Bertholet:void-def
Open

Redefine c_void#159935
Jules-Bertholet wants to merge 2 commits into
rust-lang:mainfrom
Jules-Bertholet:void-def

Conversation

@Jules-Bertholet

@Jules-Bertholet Jules-Bertholet commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

The current definition is based on legacy cruft which no longer matter. Let's make it somewhat saner:

  • Outside Miri: be maximally accepting, use UnsafePinned and MaybeUninit to make it as difficult as possible to cause UB with the type.
  • Inside Miri: make c_void uninhabited. That way, any attempt to construct a value of c_void, or a reference to it, gets flagged. (Such code is essentially guaranteed to be wrong, as c_void should only be used behind a raw pointer. However, people love to do it anyway. We should add a lint at some point…)

@rustbot label A-FFI A-miri

@rustbot rustbot added A-test-infra-minicore Area: `minicore` test auxiliary and `//@ add-core-stubs` S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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. T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue. labels Jul 25, 2026
@Jules-Bertholet
Jules-Bertholet force-pushed the void-def branch 3 times, most recently from e913fbe to 399567c Compare July 26, 2026 00:43
@rustbot rustbot added A-FFI Area: Foreign function interface (FFI) A-miri Area: The miri tool labels Jul 26, 2026
@theemathas

Copy link
Copy Markdown
Contributor

The current definition is based on legacy cruft
which no longer matters. Redefine the type
to be maximally forgiving in most cases,
but maximally strict against invalid usage
in Miri.
@theemathas

theemathas commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

This change:

  • makes c_void no longer implement Freeze, which I think affects consteval stuff
  • makes c_void uninhabited in miri, which affects pattern matching and maybe borrow-checking (and maybe layout?)

@Jules-Bertholet

Jules-Bertholet commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

makes c_void no longer implement Freeze, which I think affects consteval stuff

You would have to obtain a value of type c_void for this to matter, and there has never been a sound way to do that, because the niche layout is unstable.

makes c_void uninhabited in miri, which affects pattern matching and maybe borrow-checking

It's not visibly uninhabited, so it shouldn't affect what code compiles (e.g. fn foo(v: c_void) { match v {} } still won't compile).

(and maybe layout?)

Yes, repr(Rust) enums which contain a field of type c_void (or a reference to it) may become smaller.

@clarfonthey

Copy link
Copy Markdown
Contributor

You would have to obtain a value of type c_void for this to matter, and there has never been a sound way to do that, because the layout was unstable.

I feel like based upon the definition, you could technically use MaybeUninit::zeroed().assume_init(), but that would obviously be a bad idea. I think that for practical purposes, code that does this is doing something wrong, but, I'm hesitant to say that this still doesn't matter.

@asquared31415

Copy link
Copy Markdown
Contributor

You would have to know that 0 was a valid discriminant, which I believe was not observable, and could have changed at any time, if std wanted to.

@Jules-Bertholet
Jules-Bertholet marked this pull request as ready for review July 26, 2026 02:43
@rustbot

rustbot commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

Any special-casing of Miri in the standard library requires review.

cc @rust-lang/miri

miri is developed in its own repository. If the Miri part of this change can be broken out, consider making this change to rust-lang/miri instead. However, if Miri needs adjusting for rustc changes, just ignore this message.

cc @rust-lang/miri

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 26, 2026
@rustbot

rustbot commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

r? @Darksonn

rustbot has assigned @Darksonn.
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: libs
  • libs expanded to 12 candidates
  • Random selection from Darksonn, JohnTitor, Mark-Simulacrum, clarfonthey, jhpratt

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job x86_64-gnu-tools failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
...............................F.................. (50/144)
.................................................. (100/144)
............................................       (144/144)

======== tests/rustdoc-gui/go-to-collapsed-elem.goml ========

[ERROR] line 40
    at `tests/rustdoc-gui/go-to-collapsed-elem.goml` line 21: Error: Node is either not clickable or not an Element: for command `click: "//*[@id='search']//a[@href='../test_docs/struct.Foo.html#method.must_use']"`
    at <file:///checkout/obj/build/x86_64-unknown-linux-gnu/test/rustdoc-gui/doc/test_docs/struct.Foo.html?search=t_use>


<= doc-ui tests done: 143 succeeded, 1 failed, 0 filtered out

Error: ()

@Darksonn

Copy link
Copy Markdown
Member

I've verified that this change doesn't affect the CFI sanitizer, which is good. I added #159950 for some missing tests in this regard.

)]
#[doc(hidden)]
__variant2,
pub struct c_void {

@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.

This changes the doc path for this type from core/ffi/enum.c_void.html to core/ffi/struct.c_void.html. If we make this change, we should introduce a redirect to keep links to the old path working.

View changes since the review

Comment on lines +63 to +65
// for backward compatibility.
#[stable(feature = "core_c_void", since = "1.30.0")]
impl RefUnwindSafe for c_void {}

@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.

This PR removes the following trait implementations:

  • Freeze
  • Unpin
  • UnsafeUnpin

One of these is a stable trait, so this is a breaking change. Should we also implement Unpin?

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.

Yes, that was an oversight, good catch

@RalfJung

Copy link
Copy Markdown
Member

I think it is a bad idea to make this type depend on cfg(miri). That will mean we don't actually UB-check the code people run.

Comment on lines +51 to +53
// However, if running in Miri,
// we want to maximize detection of UB,
// so we make `c_void` uninhabited.

@RalfJung RalfJung 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.

I don't think this is worth the risk of divergence from the behavior during regular compilation.

View changes since the review

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

Compare the risks of not doing this. Either we:

  • Use the permissive definition everywhere, so Miri stops flagging misuse that happens in practice and that it currently flags, and such misuse becomes more common
  • Use the strict definition everywhere, so code that in the past was UB or almost-UB but happened to work, now triggers real nasal demons
  • Stick with the current fragile middle ground, where there is a bunch of almost-UB—which Miri won't flag, potentially giving people false confidence, but it's very fragile and easy to turn into full UB

I think the theoretical downside of us maybe having overlooked some way in which the cfg(miri) version has less UB, is outweighed by these practical and certain downsides.

@RalfJung RalfJung 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.

Miri is not a general tool to detect misuse. It is a very specific tool to detect language UB. Whatever misuse they do is apparently not language UB, so Miri is the wrong tool to find it. I am fine with having extra checks under if cfg!(miri) to detect bugs early (though usually those should then probably also be enabled as debug assertions), as those are obviously correct. But I am not fine with using entirely different type definitions under cfg!(miri).

I understand your arguments in favor of this change, but I don't think they are convincing enough. There are way too many things that happen inside the compiler that may go different for those different type definitions, that's just not a game I am willing to play. That's still a clear "thanks but no" from my side for any cfg!(miri) in this type definition.

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

Whatever misuse they do is apparently not language UB

Plenty is! And plenty isn't only because of unspecified details of the current layout.

If you don't like the approach this PR takes, which of the alternatives I listed would you prefer?

@RalfJung RalfJung 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.

I think that's up to libs or libs-api to decide, I don't have a strong opinion beyond "Miri should check the actual code we usually run".

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.

"Miri should check the actual code we usually run".

Note that this is almost certainly not happening anyway, because c_void is intended for FFI, so code using it run under Miri is likely using some sort of mocking.

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 needs to mock the C side, but the Rust side should be faithful.

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.

How would you feel about making the cfg(miri) definition identical to the cfg(not(miri)), except for an additional !-typed field?

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.

That's still far from identical as such a field makes quite the difference for layout computation.

Might might do ill-advised nonsense like Option<c_void> and whatever. I don't want that to start doing things in Miri that it wouldn't do outside Miri.

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.

And we can't just make it straight-up uninhabited always, right? Because that breaks other stuff/makes it incorrect? (I haven't thought about this long enough to remember the answer, it's out of cache apparently.)

)]
#[doc(hidden)]
__variant2,
pub struct c_void {

@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.

mejrs makes a good point on zulip

It's also a breaking change to change it back from a struct to enum if we ever need to, even if all fields are private you can make a c_void { .. } pattern

Let's add a test using this pattern so that we catch it when/if this changes. (Even if we don't make this change, we should add the test.)

View changes since the review

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

If you are doing this, that means you a
are matching on a value or reference to c_void, which means your code is essentially guaranteed to be wrong (UB or near-UB).

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.

Not necessarily. The code might just be dead code.

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jul 26, 2026
…rtdev

Add CFI tests for return types and never type

In a few different conversations, the CFI types of various functions has come up. First, the CFI type of returning `!` came up in rust-lang#159446 (comment), where we found that it doesn't have the same CFI type as returning `()`, which it probably should.

Then in rust-lang#159935, I wanted to make sure that this does not change the CFI type of functions when `*mut c_void` appears as an argument. Luckily it does not since it's a lang item, but there's no test for this case.

Thus, add tests for all of these cases and a few others.

AI assistance was involved with writing the test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-FFI Area: Foreign function interface (FFI) A-miri Area: The miri tool A-test-infra-minicore Area: `minicore` test auxiliary and `//@ add-core-stubs` 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. T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants