diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7ae8b027e3e5c..288bf149dbed1 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1505,7 +1505,11 @@ impl BackendRepr { #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum Variants { /// Single enum variants, structs/tuples, unions, and all non-ADTs. - Single { index: VariantIdx }, + Single { + /// The index of the only (inhabited) variant. Can be an invalid index if there + /// is no variant! + index: VariantIdx + }, /// Enum-likes with more than one variant: each variant comes with /// a *discriminant* (usually the same as the variant index but the user can diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index c7c8a2902e299..6faac1582ab8d 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -70,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if ty.is_enum() { // Hilariously, `Single` is used even for 0-variant enums. // (See https://github.com/rust-lang/rust/issues/89765). - if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { + if ty.ty_adt_def().unwrap().variants().is_empty() { throw_ub!(UninhabitedEnumVariantRead(index)) } // For consistency with `write_discriminant`, and to make sure that diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0f2a6d598a0fd..75cd0c0dd46cd 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1086,6 +1086,8 @@ rustc_queries! { } /// Computes the tag (if any) for a given type and variant. + /// `None` means that the variant doesn't need a tag (because it is niched). + /// Will panic for uninhabited variants. query tag_for_variant( key: (Ty<'tcx>, abi::VariantIdx) ) -> Option { diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index f19a567cd8490..83463babc4f48 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -319,38 +319,35 @@ pub(crate) mod rustc { ) -> Result { assert!(def.is_enum()); - // Computes the variant of a given index. - let layout_of_variant = |index, encoding: Option>| { - let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); - let variant_def = Def::Variant(def.variant(index)); - let variant_layout = ty_variant(cx, (ty, layout), index); - Self::from_variant( - variant_def, - tag.map(|tag| (tag, index, encoding.unwrap())), - (ty, variant_layout), - layout.size, - cx, - ) - }; + // Computes the layout of a variant. + let layout_of_variant = + |index, encoding: Option>| -> Result { + let variant_layout = ty_variant(cx, (ty, layout), index); + if variant_layout.is_uninhabited() { + return Ok(Self::uninhabited()); + } + let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index)); + let variant_def = Def::Variant(def.variant(index)); + Self::from_variant( + variant_def, + tag.map(|tag| (tag, index, encoding.unwrap())), + (ty, variant_layout), + layout.size, + cx, + ) + }; - // We consider three kinds of enums, each demanding a different - // treatment of their layout computation: - // 1. enums that are uninhabited ZSTs - // 2. enums that delegate their layout to a variant - // 3. enums with multiple variants match layout.variants() { - Variants::Single { .. } if layout.is_uninhabited() && layout.size == Size::ZERO => { - // The layout representation of uninhabited, ZST enums is - // defined to be like that of the `!` type, as opposed of a - // typical enum. Consequently, they cannot be descended into - // as if they typical enums. We therefore special-case this - // scenario and simply return an uninhabited `Tree`. - Ok(Self::uninhabited()) - } Variants::Single { index } => { - // `Variants::Single` on enums with variants denotes that - // the enum delegates its layout to the variant at `index`. - layout_of_variant(*index, None) + // Hilariously, `Single` is used even for 0-variant enums; + // `index` is just junk in that case. + if ty.ty_adt_def().unwrap().variants().is_empty() { + Ok(Self::uninhabited()) + } else { + // `Variants::Single` on enums with variants denotes that + // the enum delegates its layout to the variant at `index`. + layout_of_variant(*index, None) + } } Variants::Multiple { tag, tag_encoding, tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple @@ -369,7 +366,7 @@ pub(crate) mod rustc { }, )?; - return Ok(Self::def(Def::Adt(def)).then(variants)); + Ok(Self::def(Def::Adt(def)).then(variants)) } } } diff --git a/tests/crashes/126267.rs b/tests/crashes/126267.rs deleted file mode 100644 index 728578179ed36..0000000000000 --- a/tests/crashes/126267.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ known-bug: rust-lang/rust#126267 - -#![feature(transmutability)] -#![crate_type = "lib"] - -pub enum ApiError {} -pub struct TokioError { - b: bool, -} -pub enum Error { - Api { source: ApiError }, - Ethereum, - Tokio { source: TokioError }, -} - -mod assert { - use std::mem::TransmuteFrom; - - pub fn is_transmutable() - where - Dst: TransmuteFrom, // safety is NOT assumed - { - } -} - -fn test() { - struct Src; - type Dst = Error; - assert::is_transmutable::(); -} diff --git a/tests/ui/transmutability/uninhabited.rs b/tests/ui/transmutability/uninhabited.rs index 74f7a1a2e898f..32f8b2290a6d4 100644 --- a/tests/ui/transmutability/uninhabited.rs +++ b/tests/ui/transmutability/uninhabited.rs @@ -16,6 +16,11 @@ mod assert { } }> {} + + pub fn is_transmutable() + where + Dst: TransmuteFrom, // safety is NOT assumed, but proven + {} } fn void() { @@ -91,3 +96,19 @@ fn distant_void() { assert::is_maybe_transmutable::(); assert::is_maybe_transmutable::(); //~ ERROR: cannot be safely transmuted } + +fn issue_126267() { + pub enum ApiError {} + pub struct TokioError { + b: bool, + } + pub enum Error { + Api { source: ApiError }, // this variant is uninhabited + Ethereum, + Tokio { source: TokioError }, + } + + struct Src; + type Dst = Error; + assert::is_transmutable::(); //~ERROR: cannot be safely transmuted +} diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 3fa02f0867ccb..2597915596e30 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -1,29 +1,29 @@ error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:41:9 + --> $DIR/uninhabited.rs:46:9 | LL | assert!(false); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:46:9 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:63:9 + --> $DIR/uninhabited.rs:68:9 | LL | assert!(false); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:63:9 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:68:9 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited.rs:87:9 + --> $DIR/uninhabited.rs:92:9 | LL | assert!(false); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:87:9 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:92:9 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `()` cannot be safely transmuted into `void::Void` - --> $DIR/uninhabited.rs:29:41 + --> $DIR/uninhabited.rs:34:41 | LL | assert::is_maybe_transmutable::<(), Void>(); | ^^^^ `void::Void` is uninhabited @@ -45,7 +45,7 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0277]: `()` cannot be safely transmuted into `yawning_void_struct::Void` - --> $DIR/uninhabited.rs:49:41 + --> $DIR/uninhabited.rs:54:41 | LL | assert::is_maybe_transmutable::<(), Void>(); | ^^^^ `yawning_void_struct::Void` is uninhabited @@ -67,7 +67,7 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` - --> $DIR/uninhabited.rs:71:41 + --> $DIR/uninhabited.rs:76:41 | LL | assert::is_maybe_transmutable::<(), Void>(); | ^^^^ `yawning_void_enum::Void` is uninhabited @@ -89,7 +89,7 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` - --> $DIR/uninhabited.rs:92:43 + --> $DIR/uninhabited.rs:97:43 | LL | assert::is_maybe_transmutable::(); | ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid` @@ -110,7 +110,22 @@ LL | | } LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` -error: aborting due to 7 previous errors +error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` + --> $DIR/uninhabited.rs:113:36 + | +LL | assert::is_transmutable::(); + | ^^^ `issue_126267::Error` may carry safety invariants + | +note: required by a bound in `is_transmutable` + --> $DIR/uninhabited.rs:22:18 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, // safety is NOT assumed, but proven + | ^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error: aborting due to 8 previous errors Some errors have detailed explanations: E0080, E0277. For more information about an error, try `rustc --explain E0080`.