@@ -7,16 +7,16 @@ use std::fmt::Write;
77use rustc_errors:: ErrorReported ;
88use rustc_hir:: def:: Namespace ;
99use rustc_macros:: HashStable ;
10- use rustc_middle:: ty:: layout:: { IntegerExt , PrimitiveExt , TyAndLayout } ;
10+ use rustc_middle:: ty:: layout:: { PrimitiveExt , TyAndLayout } ;
1111use rustc_middle:: ty:: print:: { FmtPrinter , PrettyPrinter , Printer } ;
1212use rustc_middle:: ty:: Ty ;
1313use rustc_middle:: { mir, ty} ;
14- use rustc_target:: abi:: { Abi , DiscriminantKind , HasDataLayout , Integer , LayoutOf , Size } ;
14+ use rustc_target:: abi:: { Abi , DiscriminantKind , HasDataLayout , LayoutOf , Size } ;
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.
@@ -469,6 +469,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
469469 . try_fold ( base_op, |op, elem| self . operand_projection ( op, elem) ) ?;
470470
471471 trace ! ( "eval_place_to_op: got {:?}" , * op) ;
472+ // Sanity-check the type we ended up with.
473+ debug_assert_eq ! (
474+ self . subst_from_current_frame_and_normalize_erasing_regions(
475+ place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
476+ ) ,
477+ op. layout. ty,
478+ ) ;
472479 Ok ( op)
473480 }
474481
@@ -576,98 +583,113 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
576583 /// Read discriminant, return the runtime value as well as the variant index.
577584 pub fn read_discriminant (
578585 & self ,
579- rval : OpTy < ' tcx , M :: PointerTag > ,
580- ) -> InterpResult < ' tcx , ( u128 , VariantIdx ) > {
581- trace ! ( "read_discriminant_value {:#?}" , rval. layout) ;
582-
583- let ( discr_layout, discr_kind, discr_index) = match rval. layout . variants {
586+ op : OpTy < ' tcx , M :: PointerTag > ,
587+ ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , VariantIdx ) > {
588+ trace ! ( "read_discriminant_value {:#?}" , op. layout) ;
589+
590+ // Get type and layout of the discriminant.
591+ let discr_layout = self . layout_of ( op. layout . ty . discriminant_ty ( * self . tcx ) ) ?;
592+ trace ! ( "discriminant type: {:?}" , discr_layout. ty) ;
593+
594+ // We use "discriminant" to refer to the value associated with a particular enum variant.
595+ // This is not to be confused with its "variant index", which is just determining its position in the
596+ // declared list of variants -- they can differ with explicitly assigned discriminants.
597+ // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
598+ // straight-forward (`DiscriminantKind::Tag`) or with a niche (`DiscriminantKind::Niche`).
599+ // Unfortunately, the rest of the compiler calls the latter "discriminant", too, which makes things
600+ // rather confusing.
601+ let ( tag_scalar_layout, tag_kind, tag_index) = match op. layout . variants {
584602 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) ) ;
603+ let discr = match op. layout . ty . discriminant_for_variant ( * self . tcx , index) {
604+ Some ( discr) => {
605+ // This type actually has discriminants.
606+ assert_eq ! ( discr. ty, discr_layout. ty) ;
607+ Scalar :: from_uint ( discr. val , discr_layout. size )
608+ }
609+ None => {
610+ // On a type without actual discriminants, variant is 0.
611+ assert_eq ! ( index. as_u32( ) , 0 ) ;
612+ Scalar :: from_uint ( index. as_u32 ( ) , discr_layout. size )
613+ }
614+ } ;
615+ return Ok ( ( discr, index) ) ;
591616 }
592- Variants :: Multiple { discr : ref discr_layout , ref discr_kind, discr_index, .. } => {
593- ( discr_layout , discr_kind, discr_index)
617+ Variants :: Multiple { ref discr , ref discr_kind, discr_index, .. } => {
618+ ( discr , discr_kind, discr_index)
594619 }
595620 } ;
596621
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
603- Ok ( match * discr_kind {
622+ // There are *three* layouts that come into play here:
623+ // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
624+ // the `Scalar` we return.
625+ // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
626+ // and used to interpret the value we read from the tag field.
627+ // For the return value, a cast to `discr_layout` is performed.
628+ // - The field storing the tag has a layout, which is very similar to `tag_layout` but
629+ // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
630+
631+ // Get layout for tag.
632+ let tag_layout = self . layout_of ( tag_scalar_layout. value . to_int_ty ( * self . tcx ) ) ?;
633+
634+ // Read tag and sanity-check `tag_layout`.
635+ let tag_val = self . read_immediate ( self . operand_field ( op, tag_index) ?) ?;
636+ assert_eq ! ( tag_layout. size, tag_val. layout. size) ;
637+ assert_eq ! ( tag_layout. abi. is_signed( ) , tag_val. layout. abi. is_signed( ) ) ;
638+ let tag_val = tag_val. to_scalar ( ) ?;
639+ trace ! ( "tag value: {:?}" , tag_val) ;
640+
641+ // Figure out which discriminant and variant this corresponds to.
642+ Ok ( match * tag_kind {
604643 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
627- let index = match rval. layout . ty . kind {
644+ let tag_bits = self
645+ . force_bits ( tag_val, tag_layout. size )
646+ . map_err ( |_| err_ub ! ( InvalidDiscriminant ( tag_val. erase_tag( ) ) ) ) ?;
647+ // Cast bits from tag layout to discriminant layout.
648+ let discr_val_cast = self . cast_from_scalar ( tag_bits, tag_layout, discr_layout. ty ) ;
649+ let discr_bits = discr_val_cast. assert_bits ( discr_layout. size ) ;
650+ // Convert discriminant to variant index, and catch invalid discriminants.
651+ let index = match op. layout . ty . kind {
628652 ty:: Adt ( adt, _) => {
629- adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == real_discr )
653+ adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == discr_bits )
630654 }
631655 ty:: Generator ( def_id, substs, _) => {
632656 let substs = substs. as_generator ( ) ;
633657 substs
634658 . discriminants ( def_id, self . tcx . tcx )
635- . find ( |( _, var) | var. val == real_discr )
659+ . find ( |( _, var) | var. val == discr_bits )
636660 }
637661 _ => bug ! ( "tagged layout for non-adt non-generator" ) ,
638662 }
639- . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
640- ( real_discr, index. 0 )
663+ . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( tag_val. erase_tag( ) ) ) ) ?;
664+ // Return the cast value, and the index.
665+ ( discr_val_cast, index. 0 )
641666 }
642667 DiscriminantKind :: Niche { dataful_variant, ref niche_variants, niche_start } => {
668+ // Compute the variant this niche value/"tag" corresponds to. With niche layout,
669+ // discriminant (encoded in niche/tag) and variant index are the same.
643670 let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
644671 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 ) {
672+ let variant = match tag_val. to_bits_or_ptr ( tag_layout. size , self ) {
649673 Err ( ptr) => {
650674 // The niche must be just 0 (which an inbounds pointer value never is)
651675 let ptr_valid = niche_start == 0
652676 && variants_start == variants_end
653677 && !self . memory . ptr_may_be_null ( ptr) ;
654678 if !ptr_valid {
655- throw_ub ! ( InvalidDiscriminant ( raw_discr . erase_tag( ) . into ( ) ) )
679+ throw_ub ! ( InvalidDiscriminant ( tag_val . erase_tag( ) ) )
656680 }
657- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
681+ dataful_variant
658682 }
659- Ok ( raw_discr ) => {
683+ Ok ( tag_bits ) => {
660684 // We need to use machine arithmetic to get the relative variant idx:
661- // 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) ;
665- let niche_start_val = ImmTy :: from_uint ( niche_start, discr_layout) ;
685+ // variant_index_relative = tag_val - niche_start_val
686+ let tag_val = ImmTy :: from_uint ( tag_bits, tag_layout) ;
687+ let niche_start_val = ImmTy :: from_uint ( niche_start, tag_layout) ;
666688 let variant_index_relative_val =
667- self . binary_op ( mir:: BinOp :: Sub , discr_val , niche_start_val) ?;
689+ self . binary_op ( mir:: BinOp :: Sub , tag_val , niche_start_val) ?;
668690 let variant_index_relative = variant_index_relative_val
669691 . to_scalar ( ) ?
670- . assert_bits ( discr_val . layout . size ) ;
692+ . assert_bits ( tag_val . layout . size ) ;
671693 // Check if this is in the range that indicates an actual discriminant.
672694 if variant_index_relative <= u128:: from ( variants_end - variants_start) {
673695 let variant_index_relative = u32:: try_from ( variant_index_relative)
@@ -676,20 +698,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
676698 let variant_index = variants_start
677699 . checked_add ( variant_index_relative)
678700 . expect ( "overflow computing absolute variant idx" ) ;
679- let variants_len = rval
701+ let variants_len = op
680702 . layout
681703 . ty
682704 . ty_adt_def ( )
683705 . expect ( "tagged layout for non adt" )
684706 . variants
685707 . len ( ) ;
686708 assert ! ( usize :: try_from( variant_index) . unwrap( ) < variants_len) ;
687- ( u128 :: from ( variant_index ) , VariantIdx :: from_u32 ( variant_index) )
709+ VariantIdx :: from_u32 ( variant_index)
688710 } else {
689- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
711+ dataful_variant
690712 }
691713 }
692- }
714+ } ;
715+ // Compute the size of the scalar we need to return.
716+ // No need to cast, because the variant index directly serves as discriminant and is
717+ // encoded in the tag.
718+ ( Scalar :: from_uint ( variant. as_u32 ( ) , discr_layout. size ) , variant)
693719 }
694720 } )
695721 }
0 commit comments