safe transmute: support Variants::Single enums

Previously, the implementation of `Tree::from_enum` incorrectly
treated enums with `Variants::Single` and `Variants::Multiple`
identically. This is incorrect for `Variants::Single` enums,
which delegate their layout to that of a variant with a particular
index (or no variant at all if the enum is empty).

This flaw manifested first as an ICE. `Tree::from_enum` attempted
to compute the tag of variants other than the one at
`Variants::Single`'s `index`, and fell afoul of a sanity-checking
assertion in `compiler/rustc_const_eval/src/interpret/discriminant.rs`.
This assertion is non-load-bearing, and can be removed; the routine
its in is well-behaved even without it.

With the assertion removed, the proximate issue becomes apparent:
calling `Tree::from_variant` on a variant that does not exist is
ill-defined. A sanity check the given variant has
`FieldShapes::Arbitrary` fails, and the analysis is (correctly)
aborted with `Err::NotYetSupported`.

This commit corrects this chain of failures by ensuring that
`Tree::from_variant` is not called on variants that are, as far as
layout is concerned, nonexistent. Specifically, the implementation
of `Tree::from_enum` is now partitioned into three cases:

  1. enums that are uninhabited
  2. enums for which all but one variant is uninhabited
  3. enums with multiple inhabited variants

`Tree::from_variant` is now only invoked in the third case. In the
first case, `Tree::uninhabited()` is produced. In the second case,
the layout is delegated to `Variants::Single`'s index.

Fixes #125811
This commit is contained in:
Jack Wrenn 2024-06-12 22:07:37 +00:00
parent 1d43fbbc73
commit fb662f2126
4 changed files with 81 additions and 59 deletions

View file

@ -0,0 +1,26 @@
//@ check-pass
//! Tests that we do not regress rust-lang/rust#125811
#![feature(transmutability)]
fn assert_transmutable<T>()
where
(): std::mem::BikeshedIntrinsicFrom<T>
{}
enum Uninhabited {}
enum SingleInhabited {
X,
Y(Uninhabited)
}
enum SingleUninhabited {
X(Uninhabited),
Y(Uninhabited),
}
fn main() {
assert_transmutable::<Uninhabited>();
assert_transmutable::<SingleInhabited>();
assert_transmutable::<SingleUninhabited>();
}