11//! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines).
22
33use rustc_abi:: { self as abi, TagEncoding , VariantIdx , Variants } ;
4- use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
4+ use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt , TyAndLayout } ;
55use rustc_middle:: ty:: { self , CoroutineArgsExt , ScalarInt , Ty } ;
66use rustc_middle:: { mir, span_bug} ;
77use tracing:: { instrument, trace} ;
@@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
2121 variant_index : VariantIdx ,
2222 dest : & impl Writeable < ' tcx , M :: Provenance > ,
2323 ) -> InterpResult < ' tcx > {
24- // Layout computation excludes uninhabited variants from consideration
25- // therefore there's no way to represent those variants in the given layout.
26- // Essentially, uninhabited variants do not have a tag that corresponds to their
27- // discriminant, so we cannot do anything here.
28- // When evaluating we will always error before even getting here, but ConstProp 'executes'
29- // dead code, so we cannot ICE here.
30- if dest. layout ( ) . for_variant ( self , variant_index) . is_uninhabited ( ) {
31- throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
32- }
33-
34- match self . tag_for_variant ( dest. layout ( ) . ty , variant_index) ? {
24+ match self . tag_for_variant ( dest. layout ( ) , variant_index) ? {
3525 Some ( ( tag, tag_field) ) => {
3626 // No need to validate that the discriminant here because the
3727 // `TyAndLayout::for_variant()` call earlier already checks the
@@ -80,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
8070 if ty. is_enum ( ) {
8171 // Hilariously, `Single` is used even for 0-variant enums.
8272 // (See https://github.com/rust-lang/rust/issues/89765).
83- if matches ! ( ty. kind ( ) , ty :: Adt ( def , .. ) if def . variants( ) . is_empty( ) ) {
73+ if ty. ty_adt_def ( ) . unwrap ( ) . variants ( ) . is_empty ( ) {
8474 throw_ub ! ( UninhabitedEnumVariantRead ( index) )
8575 }
8676 // For consistency with `write_discriminant`, and to make sure that
@@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
188178 let variants =
189179 ty. ty_adt_def ( ) . expect ( "tagged layout for non adt" ) . variants ( ) ;
190180 assert ! ( variant_index < variants. next_index( ) ) ;
181+ if variant_index == untagged_variant {
182+ // The untagged variant can be in the niche range, but even then it
183+ // is not a valid encoding.
184+ throw_ub ! ( InvalidTag ( Scalar :: from_uint( tag_bits, tag_layout. size) ) )
185+ }
191186 variant_index
192187 } else {
193188 untagged_variant
@@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
236231 /// given field index.
237232 pub ( crate ) fn tag_for_variant (
238233 & self ,
239- ty : Ty < ' tcx > ,
234+ layout : TyAndLayout < ' tcx > ,
240235 variant_index : VariantIdx ,
241236 ) -> InterpResult < ' tcx , Option < ( ScalarInt , usize ) > > {
242- match self . layout_of ( ty) ?. variants {
237+ // Layout computation excludes uninhabited variants from consideration.
238+ // Therefore, there's no way to represent those variants in the given layout.
239+ // Essentially, uninhabited variants do not have a tag that corresponds to their
240+ // discriminant, so we have to bail out here.
241+ if layout. for_variant ( self , variant_index) . is_uninhabited ( ) {
242+ throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
243+ }
244+
245+ match layout. variants {
243246 abi:: Variants :: Single { .. } => {
244247 // The tag of a `Single` enum is like the tag of the niched
245248 // variant: there's no tag as the discriminant is encoded
@@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
260263 // raw discriminants for enums are isize or bigger during
261264 // their computation, but the in-memory tag is the smallest possible
262265 // representation
263- let discr = self . discriminant_for_variant ( ty, variant_index) ?;
266+ let discr = self . discriminant_for_variant ( layout . ty , variant_index) ?;
264267 let discr_size = discr. layout . size ;
265268 let discr_val = discr. to_scalar ( ) . to_bits ( discr_size) ?;
266269 let tag_size = tag_layout. size ( self ) ;
@@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
286289 ..
287290 } => {
288291 assert ! ( variant_index != untagged_variant) ;
292+ // We checked that this variant is inhabited, so it must be in the niche range.
293+ assert ! (
294+ niche_variants. contains( & variant_index) ,
295+ "invalid variant index for this enum"
296+ ) ;
289297 let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
290- let variant_index_relative = variant_index
291- . as_u32 ( )
292- . checked_sub ( variants_start)
293- . expect ( "overflow computing relative variant idx" ) ;
298+ let variant_index_relative = variant_index. as_u32 ( ) . strict_sub ( variants_start) ;
294299 // We need to use machine arithmetic when taking into account `niche_start`:
295300 // tag_val = variant_index_relative + niche_start_val
296301 let tag_layout = self . layout_of ( tag_layout. primitive ( ) . to_int_ty ( * self . tcx ) ) ?;
0 commit comments