@@ -15,8 +15,8 @@ use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, Integer, LayoutOf,
1515use rustc_target:: abi:: { VariantIdx , Variants } ;
1616
1717use super :: {
18- from_known_layout, sign_extend , truncate , ConstValue , GlobalId , InterpCx , InterpResult ,
19- MPlaceTy , Machine , MemPlace , Place , PlaceTy , Pointer , Scalar , ScalarMaybeUninit ,
18+ from_known_layout, ConstValue , GlobalId , InterpCx , InterpResult , MPlaceTy , Machine , MemPlace ,
19+ Place , PlaceTy , Pointer , Scalar , ScalarMaybeUninit ,
2020} ;
2121
2222/// An `Immediate` represents a single immediate self-contained Rust value.
@@ -577,91 +577,112 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
577577 pub fn read_discriminant (
578578 & self ,
579579 rval : OpTy < ' tcx , M :: PointerTag > ,
580- ) -> InterpResult < ' tcx , ( u128 , VariantIdx ) > {
580+ ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , VariantIdx ) > {
581581 trace ! ( "read_discriminant_value {:#?}" , rval. layout) ;
582582
583- let ( discr_layout , discr_kind, discr_index) = match rval. layout . variants {
583+ let ( discr_scalar_layout , discr_kind, discr_index) = match rval. layout . variants {
584584 Variants :: Single { index } => {
585- let discr_val = rval
586- . layout
587- . ty
588- . discriminant_for_variant ( * self . tcx , index)
589- . map_or ( u128:: from ( index. as_u32 ( ) ) , |discr| discr. val ) ;
590- return Ok ( ( discr_val, index) ) ;
585+ let discr = match rval. layout . ty . discriminant_for_variant ( * self . tcx , index) {
586+ Some ( discr) => {
587+ // This type actually has discriminants.
588+ let discr_layout = self . layout_of ( discr. ty ) ?;
589+ Scalar :: from_uint ( discr. val , discr_layout. size )
590+ }
591+ None => {
592+ // On a type without actual discriminants, return variant idx as `u8`.
593+ let discr_layout = self . layout_of ( self . tcx . types . u8 ) ?;
594+ Scalar :: from_uint ( index. as_u32 ( ) , discr_layout. size )
595+ }
596+ } ;
597+ return Ok ( ( discr, index) ) ;
591598 }
592- Variants :: Multiple { discr : ref discr_layout , ref discr_kind, discr_index, .. } => {
593- ( discr_layout , discr_kind, discr_index)
599+ Variants :: Multiple { ref discr , ref discr_kind, discr_index, .. } => {
600+ ( discr , discr_kind, discr_index)
594601 }
595602 } ;
596603
597- // read raw discriminant value
598- let discr_op = self . operand_field ( rval, discr_index) ?;
599- let discr_val = self . read_immediate ( discr_op) ?;
600- let raw_discr = discr_val. to_scalar_or_undef ( ) ;
601- trace ! ( "discr value: {:?}" , raw_discr) ;
602- // post-process
604+ // There are *three* types/layouts that come into play here:
605+ // - The field storing the discriminant has a layout, which my be a pointer.
606+ // This is `discr_val.layout`; we just use it for sanity checks.
607+ // - The discriminant has a layout for tag storing purposes, which is always an integer.
608+ // This is `discr_layout` and is used to interpret the value we read from the
609+ // discriminant field.
610+ // - The discriminant also has a type for typechecking, and that type's
611+ // layout can be *different*. This is `discr_ty`, and is used for the `Scalar`
612+ // we return. If necessary, a cast from `discr_layout` is performed.
613+
614+ // Get layout for tag.
615+ let discr_layout = self . layout_of ( discr_scalar_layout. value . to_int_ty ( * self . tcx ) ) ?;
616+
617+ // Read discriminant value and sanity-check `discr_layout`.
618+ let discr_val = self . read_immediate ( self . operand_field ( rval, discr_index) ?) ?;
619+ assert_eq ! ( discr_layout. size, discr_val. layout. size) ;
620+ assert_eq ! ( discr_layout. abi. is_signed( ) , discr_val. layout. abi. is_signed( ) ) ;
621+ let discr_val = discr_val. to_scalar ( ) ?;
622+ trace ! ( "discriminant value: {:?}" , discr_val) ;
623+
624+ // Get type used by typechecking.
625+ let discr_ty = match rval. layout . ty . kind {
626+ ty:: Adt ( adt, _) => {
627+ let discr_int_ty = Integer :: from_attr ( self , adt. repr . discr_type ( ) ) ;
628+ // The signedness of tag and discriminant is the same.
629+ discr_int_ty. to_ty ( * self . tcx , discr_layout. abi . is_signed ( ) )
630+ }
631+ ty:: Generator ( _, substs, _) => {
632+ let substs = substs. as_generator ( ) ;
633+ substs. discr_ty ( * self . tcx )
634+ }
635+ _ => bug ! ( "multiple variants for non-adt non-generator" ) ,
636+ } ;
637+
638+ // Figure out which discriminant and variant this corresponds to.
603639 Ok ( match * discr_kind {
604640 DiscriminantKind :: Tag => {
605- let bits_discr = raw_discr
606- . not_undef ( )
607- . and_then ( |raw_discr| self . force_bits ( raw_discr, discr_val. layout . size ) )
608- . map_err ( |_| err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
609- let real_discr = if discr_val. layout . abi . is_signed ( ) {
610- // going from layout tag type to typeck discriminant type
611- // requires first sign extending with the discriminant layout
612- let sexted = sign_extend ( bits_discr, discr_val. layout . size ) ;
613- // and then zeroing with the typeck discriminant type
614- let discr_ty = rval
615- . layout
616- . ty
617- . ty_adt_def ( )
618- . expect ( "tagged layout corresponds to adt" )
619- . repr
620- . discr_type ( ) ;
621- let size = Integer :: from_attr ( self , discr_ty) . size ( ) ;
622- truncate ( sexted, size)
623- } else {
624- bits_discr
625- } ;
626- // Make sure we catch invalid discriminants
641+ let discr_bits = self
642+ . force_bits ( discr_val, discr_layout. size )
643+ . map_err ( |_| err_ub ! ( InvalidDiscriminant ( discr_val. erase_tag( ) ) ) ) ?;
644+ // Cast discriminant bits to the right type.
645+ let discr_ty_layout = self . layout_of ( discr_ty) ?;
646+ let discr_val_cast =
647+ self . cast_from_scalar ( discr_bits, discr_layout, discr_ty) ;
648+ let discr_bits = discr_val_cast. assert_bits ( discr_ty_layout. size ) ;
649+ // Find variant index for this tag, and catch invalid discriminants.
627650 let index = match rval. layout . ty . kind {
628651 ty:: Adt ( adt, _) => {
629- adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == real_discr )
652+ adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == discr_bits )
630653 }
631654 ty:: Generator ( def_id, substs, _) => {
632655 let substs = substs. as_generator ( ) ;
633656 substs
634657 . discriminants ( def_id, self . tcx . tcx )
635- . find ( |( _, var) | var. val == real_discr )
658+ . find ( |( _, var) | var. val == discr_bits )
636659 }
637660 _ => bug ! ( "tagged layout for non-adt non-generator" ) ,
638661 }
639- . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
640- ( real_discr, index. 0 )
662+ . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( discr_val. erase_tag( ) ) ) ) ?;
663+ // Return the cast value, and the index.
664+ ( discr_val_cast, index. 0 )
641665 }
642666 DiscriminantKind :: Niche { dataful_variant, ref niche_variants, niche_start } => {
667+ // Compute the variant this discriminant corresponds to. With niche layout,
668+ // tag and variant index are the same.
643669 let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
644670 let variants_end = niche_variants. end ( ) . as_u32 ( ) ;
645- let raw_discr = raw_discr
646- . not_undef ( )
647- . map_err ( |_| err_ub ! ( InvalidDiscriminant ( ScalarMaybeUninit :: Uninit ) ) ) ?;
648- match raw_discr. to_bits_or_ptr ( discr_val. layout . size , self ) {
671+ let variant = match discr_val. to_bits_or_ptr ( discr_layout. size , self ) {
649672 Err ( ptr) => {
650673 // The niche must be just 0 (which an inbounds pointer value never is)
651674 let ptr_valid = niche_start == 0
652675 && variants_start == variants_end
653676 && !self . memory . ptr_may_be_null ( ptr) ;
654677 if !ptr_valid {
655- throw_ub ! ( InvalidDiscriminant ( raw_discr . erase_tag( ) . into ( ) ) )
678+ throw_ub ! ( InvalidDiscriminant ( discr_val . erase_tag( ) ) )
656679 }
657- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
680+ dataful_variant
658681 }
659- Ok ( raw_discr ) => {
682+ Ok ( bits_discr ) => {
660683 // We need to use machine arithmetic to get the relative variant idx:
661684 // variant_index_relative = discr_val - niche_start_val
662- let discr_layout =
663- self . layout_of ( discr_layout. value . to_int_ty ( * self . tcx ) ) ?;
664- let discr_val = ImmTy :: from_uint ( raw_discr, discr_layout) ;
685+ let discr_val = ImmTy :: from_uint ( bits_discr, discr_layout) ;
665686 let niche_start_val = ImmTy :: from_uint ( niche_start, discr_layout) ;
666687 let variant_index_relative_val =
667688 self . binary_op ( mir:: BinOp :: Sub , discr_val, niche_start_val) ?;
@@ -684,12 +705,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
684705 . variants
685706 . len ( ) ;
686707 assert ! ( usize :: try_from( variant_index) . unwrap( ) < variants_len) ;
687- ( u128 :: from ( variant_index ) , VariantIdx :: from_u32 ( variant_index) )
708+ VariantIdx :: from_u32 ( variant_index)
688709 } else {
689- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
710+ dataful_variant
690711 }
691712 }
692- }
713+ } ;
714+ // Compute the size of the scalar we need to return.
715+ // FIXME: Why do we not need to do a cast here like we do above?
716+ let size = self . layout_of ( discr_ty) ?. size ;
717+ ( Scalar :: from_uint ( variant. as_u32 ( ) , size) , variant)
693718 }
694719 } )
695720 }
0 commit comments