df1d6168f4
Previously, `Tree::from_enum`'s implementation branched into three disjoint cases: 1. enums that uninhabited 2. enums for which all but one variant is uninhabited 3. enums with multiple inhabited variants This branching (incorrectly) did not differentiate between variantful and variantless uninhabited enums. In both cases, we assumed (and asserted) that uninhabited enums are zero-sized types. This assumption is false for enums like: enum Uninhabited { A(!, u128) } ...which, currently, has the same size as `u128`. This faulty assumption manifested as the ICE reported in #126460. In this PR, we revise the first case of `Tree::from_enum` to consider only the narrow category of "enums that are uninhabited ZSTs". These enums, whose layouts are described with `Variants::Single { index }`, are special in their layouts otherwise resemble the `!` type and cannot be descended into like typical enums. This first case captures uninhabited enums like: enum Uninhabited { A(!, !), B(!) } The second case is revised to consider the broader category of "enums that defer their layout to one of their variants"; i.e., enums whose layouts are described with `Variants::Single { index }` and that do have a variant at `index`. This second case captures uninhabited enums that are not ZSTs, like: enum Uninhabited { A(!, u128) } ...which represent their variants with `Variants::Single`. Finally, the third case is revised to cover the broader category of "enums with multiple variants", which captures uninhabited, non-ZST enums like: enum Uninhabited { A(u8, !), B(!, u32) } ...which represent their variants with `Variants::Multiple`. This PR also adds a comment requested by RalfJung in his review of #126358 to `compiler/rustc_const_eval/src/interpret/discriminant.rs`. Fixes #126460
94 lines
2.9 KiB
Rust
94 lines
2.9 KiB
Rust
#![crate_type = "lib"]
|
|
#![feature(transmutability)]
|
|
#![allow(dead_code, incomplete_features, non_camel_case_types)]
|
|
|
|
mod assert {
|
|
use std::mem::{Assume, BikeshedIntrinsicFrom};
|
|
|
|
pub fn is_maybe_transmutable<Src, Dst>()
|
|
where
|
|
Dst: BikeshedIntrinsicFrom<Src, {
|
|
Assume {
|
|
alignment: true,
|
|
lifetimes: true,
|
|
safety: true,
|
|
validity: true,
|
|
}
|
|
}>
|
|
{}
|
|
}
|
|
|
|
fn void() {
|
|
enum Void {}
|
|
|
|
// This transmutation is vacuously acceptable; since one cannot construct a
|
|
// `Void`, unsoundness cannot directly arise from transmuting a void into
|
|
// anything else.
|
|
assert::is_maybe_transmutable::<Void, u128>();
|
|
|
|
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
|
|
}
|
|
|
|
// Non-ZST uninhabited types are, nonetheless, uninhabited.
|
|
fn yawning_void_struct() {
|
|
enum Void {}
|
|
|
|
struct YawningVoid(Void, u128);
|
|
|
|
const _: () = {
|
|
assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
|
|
// Just to be sure the above constant actually evaluated:
|
|
assert!(false); //~ ERROR: evaluation of constant value failed
|
|
};
|
|
|
|
// This transmutation is vacuously acceptable; since one cannot construct a
|
|
// `Void`, unsoundness cannot directly arise from transmuting a void into
|
|
// anything else.
|
|
assert::is_maybe_transmutable::<YawningVoid, u128>();
|
|
|
|
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
|
|
}
|
|
|
|
// Non-ZST uninhabited types are, nonetheless, uninhabited.
|
|
fn yawning_void_enum() {
|
|
enum Void {}
|
|
|
|
enum YawningVoid {
|
|
A(Void, u128),
|
|
}
|
|
|
|
const _: () = {
|
|
assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
|
|
// Just to be sure the above constant actually evaluated:
|
|
assert!(false); //~ ERROR: evaluation of constant value failed
|
|
};
|
|
|
|
// This transmutation is vacuously acceptable; since one cannot construct a
|
|
// `Void`, unsoundness cannot directly arise from transmuting a void into
|
|
// anything else.
|
|
assert::is_maybe_transmutable::<YawningVoid, u128>();
|
|
|
|
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
|
|
}
|
|
|
|
// References to uninhabited types are, logically, uninhabited, but for layout
|
|
// purposes are not ZSTs, and aren't treated as uninhabited when they appear in
|
|
// enum variants.
|
|
fn distant_void() {
|
|
enum Void {}
|
|
|
|
enum DistantVoid {
|
|
A(&'static Void)
|
|
}
|
|
|
|
const _: () = {
|
|
assert!(std::mem::size_of::<DistantVoid>() == std::mem::size_of::<usize>());
|
|
// Just to be sure the above constant actually evaluated:
|
|
assert!(false); //~ ERROR: evaluation of constant value failed
|
|
};
|
|
|
|
assert::is_maybe_transmutable::<DistantVoid, ()>();
|
|
assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
|
|
assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
|
|
}
|