diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index c5d8d758c4733..11aa18cdb224d 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -126,6 +126,13 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display { } impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Synthetize a layout representing the variant-specific fields of an enum-like layout. + /// + /// Note that the resulting layout *does not* fully describes `self.ty` at that specific + /// variant: prefix fields (e.g. in coroutines) and tag information are lost. + /// + /// If you don't need type information about the variant's fields, prefer using + /// `self.layout.variants` directly. pub fn for_variant(self, cx: &C, variant_index: VariantIdx) -> Self where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e0e9ecaa49c63..1e0fd78b4dd75 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -2203,6 +2203,17 @@ impl LayoutData { pub fn is_uninhabited(&self) -> bool { self.uninhabited } + + /// Returns `true` if the given variant is uninhabited. + pub fn is_variant_uninhabited(&self, variant: VariantIdx) -> bool { + match self.variants { + Variants::Empty => true, + Variants::Single { index } => variant != index || self.uninhabited, + Variants::Multiple { ref variants, .. } => { + variants.get(variant).map(|v| v.uninhabited).unwrap_or(true) + } + } + } } impl fmt::Debug for LayoutData diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index 8818e8634952e..fd4f1d8c61e55 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -14,7 +14,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( variant_index: VariantIdx, ) { let layout = place.layout(); - if layout.for_variant(fx, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { return; } match layout.variants { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index b592e4a339346..14a5f71fbceaa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -477,7 +477,7 @@ pub(super) fn codegen_tag_value<'tcx, V>( ) -> Result, UninhabitedVariantError> { // By checking uninhabited-ness first we don't need to worry about types // like `(u32, !)` which are single-variant but weird. - if layout.for_variant(cx, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { return Err(UninhabitedVariantError); } diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index a1776c6ba3d13..9d0499102c08e 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -210,7 +210,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Reading the discriminant of an uninhabited variant is UB. This is the basis for the // `uninhabited_enum_branching` MIR pass. It also ensures consistency with // `write_discriminant`. - if op.layout().for_variant(self, index).is_uninhabited() { + if op.layout().is_variant_uninhabited(index) { throw_ub!(UninhabitedEnumVariantRead(Some(index))) } interp_ok(index) @@ -252,7 +252,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Therefore, there's no way to represent those variants in the given layout. // Essentially, uninhabited variants do not have a tag that corresponds to their // discriminant, so we have to bail out here. - if layout.for_variant(self, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) }