pub fn read_discriminant(
        &self,
        op: &OpTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> {
        trace!("read_discriminant_value {:#?}", op.layout);
        // Get type and layout of the discriminant.
        let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
        trace!("discriminant type: {:?}", discr_layout.ty);

        // We use "discriminant" to refer to the value associated with a particular enum variant.
        // This is not to be confused with its "variant index", which is just determining its position in the
        // declared list of variants -- they can differ with explicitly assigned discriminants.
        // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
        // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
        let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
            Variants::Single { index } => {
                let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
                    Some(discr) => {
                        // This type actually has discriminants.
                        assert_eq!(discr.ty, discr_layout.ty);
                        Scalar::from_uint(discr.val, discr_layout.size)
                    }
                    None => {
                        // On a type without actual discriminants, variant is 0.
                        assert_eq!(index.as_u32(), 0);
                        Scalar::from_uint(index.as_u32(), discr_layout.size)
                    }
                };
                return Ok((discr, index));
            }
            Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => {
                (tag, tag_encoding, tag_field)
            }
        };

        // There are *three* layouts that come into play here:
        // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
        //   the `Scalar` we return.
        // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
        //   and used to interpret the value we read from the tag field.
        //   For the return value, a cast to `discr_layout` is performed.
        // - The field storing the tag has a layout, which is very similar to `tag_layout` but
        //   may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.

        // Get layout for tag.
        let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?;

        // Read tag and sanity-check `tag_layout`.
        let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
        assert_eq!(tag_layout.size, tag_val.layout.size);
        assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
        let tag_val = tag_val.to_scalar()?;
        trace!("tag value: {:?}", tag_val);

        // Figure out which discriminant and variant this corresponds to.
        Ok(match *tag_encoding {
            TagEncoding::Direct => {
                let tag_bits = self
                    .force_bits(tag_val, tag_layout.size)
                    .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?;
                // Cast bits from tag layout to discriminant layout.
                let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty);
                let discr_bits = discr_val.assert_bits(discr_layout.size);
                // Convert discriminant to variant index, and catch invalid discriminants.
                let index = match *op.layout.ty.kind() {
                    ty::Adt(adt, _) => {
                        adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
                    }
                    ty::Generator(def_id, substs, _) => {
                        let substs = substs.as_generator();
                        substs
                            .discriminants(def_id, *self.tcx)
                            .find(|(_, var)| var.val == discr_bits)
                    }
                    _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
                }
                .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?;
                // Return the cast value, and the index.
                (discr_val, index.0)
            }
            TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => {
                // Compute the variant this niche value/"tag" corresponds to. With niche layout,
                // discriminant (encoded in niche/tag) and variant index are the same.
                let variants_start = niche_variants.start().as_u32();
                let variants_end = niche_variants.end().as_u32();
                let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) {
                    Err(ptr) => {
                        // The niche must be just 0 (which an inbounds pointer value never is)
                        let ptr_valid = niche_start == 0
                            && variants_start == variants_end
                            && !self.memory.ptr_may_be_null(ptr);
                        if !ptr_valid {
                            throw_ub!(InvalidTag(tag_val.erase_tag()))
                        }
                        dataful_variant
                    }
                    Ok(tag_bits) => {
                        // We need to use machine arithmetic to get the relative variant idx:
                        // variant_index_relative = tag_val - niche_start_val
                        let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
                        let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
                        let variant_index_relative_val =
                            self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
                        let variant_index_relative = variant_index_relative_val
                            .to_scalar()?
                            .assert_bits(tag_val.layout.size);
                        // Check if this is in the range that indicates an actual discriminant.
                        if variant_index_relative <= u128::from(variants_end - variants_start) {
                            let variant_index_relative = u32::try_from(variant_index_relative)
                                .expect("we checked that this fits into a u32");
                            // Then computing the absolute variant idx should not overflow any more.
                            let variant_index = variants_start
                                .checked_add(variant_index_relative)
                                .expect("overflow computing absolute variant idx");
                            let variants_len = op
                                .layout
                                .ty
                                .ty_adt_def()
                                .expect("tagged layout for non adt")
                                .variants
                                .len();
                            assert!(usize::try_from(variant_index).unwrap() < variants_len);
                            VariantIdx::from_u32(variant_index)
                        } else {
                            dataful_variant
                        }
                    }
                };
                // Compute the size of the scalar we need to return.
                // No need to cast, because the variant index directly serves as discriminant and is
                // encoded in the tag.
                (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
            }
        })
    }
/// Read discriminant, return the runtime value as well as the variant index.
Read discriminant, return the runtime value as well as the variant index.
Read discriminant return the runtime value as well as the variant index .
pub fn read_discriminant(
        &self,
        op: &OpTy<'tcx, M::PointerTag>,
    ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, VariantIdx)> {
        trace!("read_discriminant_value {:#?}", op.layout);
        // Get type and layout of the discriminant.
        let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
        trace!("discriminant type: {:?}", discr_layout.ty);
        // We use "discriminant" to refer to the value associated with a particular enum variant.
        // This is not to be confused with its "variant index", which is just determining its position in the
        // declared list of variants -- they can differ with explicitly assigned discriminants.
        // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
        // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
        let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
            Variants::Single { index } => {
                let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
                    Some(discr) => {
                        // This type actually has discriminants.
                        assert_eq!(discr.ty, discr_layout.ty);
                        Scalar::from_uint(discr.val, discr_layout.size)
                    }
                    None => {
                        // On a type without actual discriminants, variant is 0.
                        assert_eq!(index.as_u32(), 0);
                        Scalar::from_uint(index.as_u32(), discr_layout.size)
                    }
                };
                return Ok((discr, index));
            }
            Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => {
                (tag, tag_encoding, tag_field)
            }
        };
        // There are *three* layouts that come into play here:
        // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
        //   the `Scalar` we return.
        // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
        //   and used to interpret the value we read from the tag field.
        //   For the return value, a cast to `discr_layout` is performed.
        // - The field storing the tag has a layout, which is very similar to `tag_layout` but
        //   may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
        // Get layout for tag.
        let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?;
        // Read tag and sanity-check `tag_layout`.
        let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
        assert_eq!(tag_layout.size, tag_val.layout.size);
        assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
        let tag_val = tag_val.to_scalar()?;
        trace!("tag value: {:?}", tag_val);
        // Figure out which discriminant and variant this corresponds to.
        Ok(match *tag_encoding {
            TagEncoding::Direct => {
                let tag_bits = self
                    .force_bits(tag_val, tag_layout.size)
                    .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?;
                // Cast bits from tag layout to discriminant layout.
                let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty);
                let discr_bits = discr_val.assert_bits(discr_layout.size);
                // Convert discriminant to variant index, and catch invalid discriminants.
                let index = match *op.layout.ty.kind() {
                    ty::Adt(adt, _) => {
                        adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
                    }
                    ty::Generator(def_id, substs, _) => {
                        let substs = substs.as_generator();
                        substs
                            .discriminants(def_id, *self.tcx)
                            .find(|(_, var)| var.val == discr_bits)
                    }
                    _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
                }
                .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?;
                // Return the cast value, and the index.
                (discr_val, index.0)
            }
            TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => {
                // Compute the variant this niche value/"tag" corresponds to. With niche layout,
                // discriminant (encoded in niche/tag) and variant index are the same.
                let variants_start = niche_variants.start().as_u32();
                let variants_end = niche_variants.end().as_u32();
                let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) {
                    Err(ptr) => {
                        // The niche must be just 0 (which an inbounds pointer value never is)
                        let ptr_valid = niche_start == 0
                            && variants_start == variants_end
                            && !self.memory.ptr_may_be_null(ptr);
                        if !ptr_valid {
                            throw_ub!(InvalidTag(tag_val.erase_tag()))
                        }
                        dataful_variant
                    }
                    Ok(tag_bits) => {
                        // We need to use machine arithmetic to get the relative variant idx:
                        // variant_index_relative = tag_val - niche_start_val
                        let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
                        let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
                        let variant_index_relative_val =
                            self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
                        let variant_index_relative = variant_index_relative_val
                            .to_scalar()?
                            .assert_bits(tag_val.layout.size);
                        // Check if this is in the range that indicates an actual discriminant.
                        if variant_index_relative <= u128::from(variants_end - variants_start) {
                            let variant_index_relative = u32::try_from(variant_index_relative)
                                .expect("we checked that this fits into a u32");
                            // Then computing the absolute variant idx should not overflow any more.
                            let variant_index = variants_start
                                .checked_add(variant_index_relative)
                                .expect("overflow computing absolute variant idx");
                            let variants_len = op
                                .layout
                                .ty
                                .ty_adt_def()
                                .expect("tagged layout for non adt")
                                .variants
                                .len();
                            assert!(usize::try_from(variant_index).unwrap() < variants_len);
                            VariantIdx::from_u32(variant_index)
                        } else {
                            dataful_variant
                        }
                    }
                };
                // Compute the size of the scalar we need to return.
                // No need to cast, because the variant index directly serves as discriminant and is
                // encoded in the tag.
                (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
            }
        })
    }
pub fn read_discriminant ( & self , op : & OpTy < ' tcx , M :: PointerTag > , ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , VariantIdx ) > { trace ! ( " " op layout ) ; // Get type and layout of the discriminant. let discr_layout = self . layout_of ( op . layout . ty . discriminant_ty ( * self . tcx ) ) ? ; trace ! ( " " discr_layout ty ) ; // We use "discriminant" to refer to the value associated with a particular enum variant. // This is not to be confused with its "variant index", which is just determining its position in the // declared list of variants -- they can differ with explicitly assigned discriminants. // We use "tag" to refer to how the discriminant is encoded in memory, which can be either // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). let ( tag_scalar_layout , tag_encoding , tag_field ) = match op . layout . variants { Variants :: Single { index } => { let discr = match op . layout . ty . discriminant_for_variant ( * self . tcx , index ) { Some ( discr ) => { // This type actually has discriminants. assert_eq ! ( discr ty discr_layout ty ) ; Scalar :: from_uint ( discr . val , discr_layout . size ) } None => { // On a type without actual discriminants, variant is 0. assert_eq ! ( index as_u32 ( ) 0 ) ; Scalar :: from_uint ( index . as_u32 ( ) , discr_layout . size ) } } ; return Ok ( ( discr , index ) ) ; } Variants :: Multiple { ref tag , ref tag_encoding , tag_field , .. } => { ( tag , tag_encoding , tag_field ) } } ; // There are *three* layouts that come into play here: // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for //   the `Scalar` we return. // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, //   and used to interpret the value we read from the tag field. //   For the return value, a cast to `discr_layout` is performed. // - The field storing the tag has a layout, which is very similar to `tag_layout` but //   may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. // Get layout for tag. let tag_layout = self . layout_of ( tag_scalar_layout . value . to_int_ty ( * self . tcx ) ) ? ; // Read tag and sanity-check `tag_layout`. let tag_val = self . read_immediate ( & self . operand_field ( op , tag_field ) ? ) ? ; assert_eq ! ( tag_layout size tag_val layout size ) ; assert_eq ! ( tag_layout abi is_signed ( ) tag_val layout abi is_signed ( ) ) ; let tag_val = tag_val . to_scalar ( ) ? ; trace ! ( " " tag_val ) ; // Figure out which discriminant and variant this corresponds to. Ok ( match * tag_encoding { TagEncoding :: Direct => { let tag_bits = self . force_bits ( tag_val , tag_layout . size ) . map_err ( | _ | err_ub ! ( InvalidTag ( tag_val erase_tag ( ) ) ) ) ? ; // Cast bits from tag layout to discriminant layout. let discr_val = self . cast_from_scalar ( tag_bits , tag_layout , discr_layout . ty ) ; let discr_bits = discr_val . assert_bits ( discr_layout . size ) ; // Convert discriminant to variant index, and catch invalid discriminants. let index = match * op . layout . ty . kind ( ) { ty :: Adt ( adt , _ ) => { adt . discriminants ( * self . tcx ) . find ( | ( _ , var ) | var . val == discr_bits ) } ty :: Generator ( def_id , substs , _ ) => { let substs = substs . as_generator ( ) ; substs . discriminants ( def_id , * self . tcx ) . find ( | ( _ , var ) | var . val == discr_bits ) } _ => span_bug ! ( self cur_span ( ) " " ) , } . ok_or_else ( | | err_ub ! ( InvalidTag ( tag_val erase_tag ( ) ) ) ) ? ; // Return the cast value, and the index. ( discr_val , index . 0 ) } TagEncoding :: Niche { dataful_variant , ref niche_variants , niche_start } => { // Compute the variant this niche value/"tag" corresponds to. With niche layout, // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants . start ( ) . as_u32 ( ) ; let variants_end = niche_variants . end ( ) . as_u32 ( ) ; let variant = match tag_val . to_bits_or_ptr ( tag_layout . size , self ) { Err ( ptr ) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && ! self . memory . ptr_may_be_null ( ptr ) ; if ! ptr_valid { throw_ub ! ( InvalidTag ( tag_val erase_tag ( ) ) ) } dataful_variant } Ok ( tag_bits ) => { // We need to use machine arithmetic to get the relative variant idx: // variant_index_relative = tag_val - niche_start_val let tag_val = ImmTy :: from_uint ( tag_bits , tag_layout ) ; let niche_start_val = ImmTy :: from_uint ( niche_start , tag_layout ) ; let variant_index_relative_val = self . binary_op ( mir :: BinOp :: Sub , & tag_val , & niche_start_val ) ? ; let variant_index_relative = variant_index_relative_val . to_scalar ( ) ? . assert_bits ( tag_val . layout . size ) ; // Check if this is in the range that indicates an actual discriminant. if variant_index_relative <= u128 :: from ( variants_end - variants_start ) { let variant_index_relative = u32 :: try_from ( variant_index_relative ) . expect ( " " ) ; // Then computing the absolute variant idx should not overflow any more. let variant_index = variants_start . checked_add ( variant_index_relative ) . expect ( " " ) ; let variants_len = op . layout . ty . ty_adt_def ( ) . expect ( " " ) . variants . len ( ) ; assert ! ( usize try_from ( variant_index ) unwrap ( ) variants_len ) ; VariantIdx :: from_u32 ( variant_index ) } else { dataful_variant } } } ; // Compute the size of the scalar we need to return. // No need to cast, because the variant index directly serves as discriminant and is // encoded in the tag. ( Scalar :: from_uint ( variant . as_u32 ( ) , discr_layout . size ) , variant ) } } ) }
Read discriminant, return the runtime value as well as the variant index.
Read discriminant return the runtime value as well as the variant index .
&self
op
&OpTy<'tcx, M::PointerTag>
&self
op
&OpTy<'tcx, M::PointerTag>