When matching on enums, read the discriminant even if there's only a single variant#154756
When matching on enums, read the discriminant even if there's only a single variant#154756meithecatte wants to merge 3 commits intorust-lang:mainfrom
Conversation
|
The Miri subtree was changed cc @rust-lang/miri Some changes occurred in match lowering cc @Nadrieril |
|
r? @mati865 rustbot has assigned @mati865. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| @@ -0,0 +1,25 @@ | |||
| // Like single_variant.rs, but with a non_exhaustive enum, as the generated MIR used to differ | |||
| // between these cases. | |||
There was a problem hiding this comment.
This seems like it could use revisions to avoid duplicating the code? Same for "single_variant_uninit".
There was a problem hiding this comment.
Ah, I wasn't aware that revisions are a thing! I would've done that in the first place had I known about it.
|
r? @Nadrieril Will also need t-lang discussion. Is the writeup up-to-date for that? Remember to make it self-contained and focused on the surface-language-level impact. I would suggest to lead with the example, add some comments to explain what happens, explain why it should be UB, and then move towards how this PR makes it UB. |
|
|
|
Maybe nominate it for the discussion? Or are you waiting on the author's response? |
|
@RalfJung I assume your question implies that the current PR description isn't sufficiently self-contained – indeed, I think I did a little "curse of knowledge" thing regarding this because in the few percent of my time that I've spent working on rustc, this tiny corner of the language is all I've been thinking of across the last year 😅 I'll rewrite the description with T-lang in mind. |
T-lang nominationThis PR changes matching logic to always read the discriminant of the enum being matched, even of it only has one variant. This results in more consistent behavior and makes @rustbot label +I-lang-nominated |
|
Thanks! @bors try |
This comment has been minimized.
This comment has been minimized.
When matching on enums, read the discriminant even if there's only a single variant
|
The first run here was the version of that PR that also included this change, right? @craterbot run mode=build-and-test crates=https://crater-reports.s3.amazonaws.com/pr-150681/retry-regressed-list.txt p=1 |
|
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
Yeah, this PR is pretty much just a rebase of that version of the previous PR. |
|
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
|
🎉 Experiment
Footnotes
|
|
(We looked at this in today's @rust-lang/lang meeting, but we're going to wait to look further until there's an analysis of the crater results.) |
|
@meithecatte could you look at those results (once you have time) to see which true positives remain, and possibly submit PRs to the affected crates? |
Synopsis
This PR removes a special case, so that matching on enums that only have a single variant still emits a discriminant read in MIR. This is motivated by some weirdness being caused by this special case, as documented in #147722 and #151786 and summarized below.
The PR is a follow-up to #150681, where it has been decided that this change should be split into a separate PR.
Motivation
Consider the following example (playground):
Here, we have a single-variant enum
Inner, which we're forcing to have a discriminant with#[repr(u8)], thus creating a niche that otherwise wouldn't be there. Then we create a reference toInnerwith an invalid discriminant. Thus, you would expect that matching on this reference should trigger UB. However, currently matching on a single-variant enum does not read the discriminant at all, and so Miri accepts this example.The weirdness doesn't end here, however. If you add a
#[non_exhaustive]attribute to theInnerenum, Miri starts detecting UB (playground). This is because if an enum is#[non_exhaustive], we must treat it just as if it already had multiple variants, to prevent semver hazards (and following #150681, we do this even fornon_exhaustiveenums defined in the current crate, to avoid having the operational semantics depend on which crate the code is in).In short, we are in a position where the
#[non_exhaustive]attribute can introduce UB – though arguably one that should already be there regardless.Further semver compatibility hazards
Whether the read of the discriminant happens or not can also be observed through means other than Miri, due to the Rust 2021 closure capturing behavior (playground):
This means that removing a
#[non_exhaustive]attribute can cause some other code to stop passing the borrow checker, which is quite surprising and unintuitive.Proposed changes
This PR makes it so that the MIR includes a read of the discriminant whenever we match on an enum – even if the enum happens to have a single variant (and therefore the discriminant is probably zero-sized).
This removes
#[non_exhaustive]from opsem considerations, resolves the unintuitive compatibility interactions, and adds UB where you would expect it.Fixes #147722.
Fixes #151786.
Fixes rust-lang/cargo#16417.
Other considerations
From a previous attempt at this change, we have had a couple of regressions on crater – though some of those already got addressed. The pattern that occurs in practice is a single-variant field-less enum that wasn't getting captured at all, and now becomes captured. A preliminary glance suggests that none of the issues found by the previous crater run should be hard to fix, however.