Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variants::Single: do not use invalid VariantIdx for uninhabited enums #133702

Merged
merged 4 commits into from
Dec 19, 2024

Conversation

RalfJung
Copy link
Member

@RalfJung RalfJung commented Dec 1, 2024

Stacked on top of #133681, only the last commit is new.

Currently, Variants::Single for an empty enum contains a VariantIdx of 0; looking that up in the enum variant list will ICE. That's quite confusing. So let's fix that by adding a new Variants::Empty case for types that have 0 variants.

try-job: i686-msvc

@rustbot
Copy link
Collaborator

rustbot commented Dec 1, 2024

r? @cjgillot

rustbot has assigned @cjgillot.
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

@rustbot
Copy link
Collaborator

rustbot commented Dec 1, 2024

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

This PR changes Stable MIR

cc @oli-obk, @celinval, @ouz-a

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri, @rust-lang/wg-const-eval

Some changes occurred to the CTFE machinery

cc @rust-lang/wg-const-eval

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@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. labels Dec 1, 2024
@@ -565,31 +564,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
StatementKind::SetDiscriminant { box place, variant_index } => {
let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
let enum_ty = place.ty(self.body, self.tcx).ty;
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
// nothing.
Copy link
Member Author

Choose a reason for hiding this comment

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

@cjgillot the old logic here seems unnecessarily cautious. Miri already for a while now considers it UB to "write" an untagged discriminant if the data does not currently encode that variant. So IMO we can also rely on this for optimizations, this slightly strengthening jump threading. (I thought we already rely on this for optimizations somewhere, but I am not sure.)

@@ -181,6 +181,7 @@ impl FieldsShape {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
pub enum VariantsShape {
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
// FIXME: needs to become `Option` like in the internal type.
Copy link
Member Author

Choose a reason for hiding this comment

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

@oli-obk, @celinval, @ouz-a what is the right place to note down "this should be fixed on the next breaking stable MIR update"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I ended up making this a separate variant, and hence also added that variant in stable-mir.

@bjorn3
Copy link
Member

bjorn3 commented Dec 1, 2024

Would introducing Variants::Empty instead work too? That would avoid a bunch of unwraps and allow replacing .is_uninhabited() checks with appropriate code in the Variants::Empty arm of the match.

@RalfJung
Copy link
Member Author

RalfJung commented Dec 1, 2024

I considered that, but I think that would make for a larger diff overall. Also, unless we want to enforce that all uninhabited types use Variants::Empty, we couldn't replace is_uninhabited checks.

But maybe I should actually give it a try to see how it pans out.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Dec 1, 2024

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

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@RalfJung RalfJung force-pushed the single-variant branch 2 times, most recently from a610961 to fa08c41 Compare December 1, 2024 16:48
@RalfJung
Copy link
Member Author

RalfJung commented Dec 1, 2024

Would introducing Variants::Empty instead work too? That would avoid a bunch of unwraps and allow replacing .is_uninhabited() checks with appropriate code in the Variants::Empty arm of the match.

Yeah that works pretty well. :)

if index == variant_index &&
// Don't confuse variants of uninhabited enums with the enum itself.
// For more details see https://github.com/rust-lang/rust/issues/69763.
this.fields != FieldsShape::Primitive =>
Copy link
Member Author

Choose a reason for hiding this comment

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

Now that ! and hence empty enums use Variants::Empty, this check does not seem to be needed any more. :)

@@ -1638,6 +1638,7 @@ impl Evaluator<'_> {
return Ok(0);
};
match &layout.variants {
Variants::Empty => unreachable!(),
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know why this is unreachable, but the old logic would already have panicked here in at least some of same cases since the variants[index.0] below would fail for no-variant enums.

Choose a reason for hiding this comment

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

It seems to be used only in mir evaluator, which I guess would not go into unreachable code.

@RalfJung
Copy link
Member Author

RalfJung commented Dec 4, 2024

The PR this is based on landed, so this is ready for review. :)

compiler/rustc_const_eval/src/interpret/discriminant.rs Outdated Show resolved Hide resolved
Comment on lines +252 to +253
// Types without variants use `0` as dummy variant index.
assert!(index.as_u32() == 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't the point of this PR to get rid of this hack?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. There are actually two hacks:

  • Some types don't have a concept of "variant", like tuples or primitives. Those are still Variants::Single and that makes sense, and they use index 0. I considered making this an Option but then the value 0 has to be duplicated in all codegen backends, so centralizing that in the layout logic seems better.
  • Some types are actually 0-variant types. Those are being changed in this PR.

Copy link
Member Author

@RalfJung RalfJung Dec 8, 2024

Choose a reason for hiding this comment

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

The key invariant this PR achieves is that if we have Variants::Single, and if the type has a discriminant_range, then the index is in that range. This is useful because it means if the type is an ADT, we can use the single variant index to index the variant list without ICEing. So far, this always requires a special check for empty enums, which is error-prone.

bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 18, 2024
Rollup of 8 pull requests

Successful merges:

 - rust-lang#133702 (Variants::Single: do not use invalid VariantIdx for uninhabited enums)
 - rust-lang#133926 (Fix const conditions for RPITITs)
 - rust-lang#134161 (Overhaul token cursors)
 - rust-lang#134253 (Overhaul keyword handling)
 - rust-lang#134394 (Clarify the match ergonomics 2024 migration lint's output)
 - rust-lang#134420 (refactor: replace &PathBuf with &Path to enhance generality)
 - rust-lang#134444 (Fix `x build --stage 1 std` when using cg_cranelift as the default backend)
 - rust-lang#134452 (fix(LazyCell): documentation of get[_mut] was wrong)

r? `@ghost`
`@rustbot` modify labels: rollup
@jieyouxu
Copy link
Member

jieyouxu commented Dec 18, 2024

I wonder if this changed cdb output in rollup:

@bors r-

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Dec 18, 2024
@jieyouxu
Copy link
Member

Trying to see:
@bors try

@bors
Copy link
Contributor

bors commented Dec 18, 2024

⌛ Trying commit 397ae3c with merge 48671e3...

bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 18, 2024
Variants::Single: do not use invalid VariantIdx for uninhabited enums

~~Stacked on top of rust-lang#133681, only the last commit is new.~~

Currently, `Variants::Single` for an empty enum contains a `VariantIdx` of 0; looking that up in the enum variant list will ICE. That's quite confusing. So let's fix that by adding a new `Variants::Empty` case for types that have 0 variants.

try-job: i686-msvc
@RalfJung
Copy link
Member Author

Oh gosh, do we have a windows person that knows this debug info stuff? If I can't even run the tests, how am I supposed to fix them?^^

It seems odd though since the enums mentioned there all do have inhabited variants, so nothing should change about them...

@jieyouxu
Copy link
Member

Oh gosh, do we have a windows person that knows this debug info stuff? If I can't even run the tests, how am I supposed to fix them?^^

I can try to run this locally in a moment.

@jieyouxu
Copy link
Member

jieyouxu commented Dec 18, 2024

Ok so I ran this locally, and it doesn't fail with cdb distributed as part of 10.0.26100.1742. I stared at the rollup log a bit more, and it had stuff like

tests\debuginfo\issue-13213.rs ... ignored, ignored when the debugger is cdb (Fails with exit code 0xc0000135 ("the application failed to initialize properly"))

I wonder if cdb just somehow failed to init properly... Probably requeue this PR once the try job comes back, no idea what actually went wrong... I only had a vague suspicion for this PR as this PR seemed to be the only one that actually touched debuginfo remotely 🤷

@bors
Copy link
Contributor

bors commented Dec 18, 2024

☀️ Try build successful - checks-actions
Build commit: 48671e3 (48671e3deea71b4f4b32142ef5300ebb0e2117d6)

@jieyouxu
Copy link
Member

@bors r=@oli-obk

@bors
Copy link
Contributor

bors commented Dec 18, 2024

📌 Commit 397ae3c has been approved by oli-obk

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Dec 18, 2024
@jdupak-ms
Copy link

jdupak-ms commented Dec 18, 2024 via email

@jdupak-ms
Copy link

jdupak-ms commented Dec 18, 2024 via email

@jieyouxu
Copy link
Member

jieyouxu commented Dec 18, 2024

I know those test quite well so I can look into it tomorrow morning if you have not it figured by then. There were some breaking changes in WinDbg, so that might be a factor.

Would be great if you could double-check the test if this PR fails again, at the moment I can't repro the failure, so it might be spurious somehow in the rollup...

bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 19, 2024
Rollup of 7 pull requests

Successful merges:

 - rust-lang#133702 (Variants::Single: do not use invalid VariantIdx for uninhabited enums)
 - rust-lang#134427 (ci: remove duplicate task definition)
 - rust-lang#134432 (Fix intra doc links not generated inside footnote definitions)
 - rust-lang#134437 (reduce compiler `Assemble` complexity)
 - rust-lang#134474 (Forbid overwriting types in typeck)
 - rust-lang#134477 (move lint_unused_mut into sub-fn)
 - rust-lang#134491 (Some destructor/drop related tweaks)

r? `@ghost`
`@rustbot` modify labels: rollup
@jdupak-ms
Copy link

I know those test quite well so I can look into it tomorrow morning if you have not it figured by then. There were some breaking changes in WinDbg, so that might be a factor.

Would be great if you could double-check the test if this PR fails again, at the moment I can't repro the failure, so it might be spurious somehow in the rollup...

Everything is green for me.

@bors bors merged commit 2a43ce0 into rust-lang:master Dec 19, 2024
7 checks passed
@rustbot rustbot added this to the 1.85.0 milestone Dec 19, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Dec 19, 2024
Rollup merge of rust-lang#133702 - RalfJung:single-variant, r=oli-obk

Variants::Single: do not use invalid VariantIdx for uninhabited enums

~~Stacked on top of rust-lang#133681, only the last commit is new.~~

Currently, `Variants::Single` for an empty enum contains a `VariantIdx` of 0; looking that up in the enum variant list will ICE. That's quite confusing. So let's fix that by adding a new `Variants::Empty` case for types that have 0 variants.

try-job: i686-msvc
@RalfJung RalfJung deleted the single-variant branch December 20, 2024 08:31
bors pushed a commit to rust-lang-ci/rust that referenced this pull request Jan 5, 2025
Variants::Single: do not use invalid VariantIdx for uninhabited enums

~~Stacked on top of rust-lang#133681, only the last commit is new.~~

Currently, `Variants::Single` for an empty enum contains a `VariantIdx` of 0; looking that up in the enum variant list will ICE. That's quite confusing. So let's fix that by adding a new `Variants::Empty` case for types that have 0 variants.

try-job: i686-msvc
remi-delmas-3000 pushed a commit to remi-delmas-3000/kani that referenced this pull request Jan 6, 2025
Propagated from rust-lang/rust#133702.
Empty enums now have `Variants::Empty` as variants instead of
`Variants::Single{ index: None }`.
remi-delmas-3000 pushed a commit to remi-delmas-3000/kani that referenced this pull request Jan 7, 2025
Propagated from rust-lang/rust#133702.
Empty enums now have `Variants::Empty` as variants instead of
`Variants::Single{ index: None }`.
remi-delmas-3000 pushed a commit to remi-delmas-3000/kani that referenced this pull request Jan 7, 2025
Propagated from rust-lang/rust#133702.
Empty enums now have `Variants::Empty` as variants instead of
`Variants::Single{ index: None }`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants