summary refs log tree commit diff
path: root/compiler/rustc_const_eval/src/interpret
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-03-22 20:31:29 +0100
committerGitHub <noreply@github.com>2024-03-22 20:31:29 +0100
commit96be3e7cc8dc1b4f38f66c66f3fcfe35762a48a6 (patch)
tree4d4ff7e02b8855bc22093cc03efac10222e10675 /compiler/rustc_const_eval/src/interpret
parent4e594572b44a06f8b63529452a08730f9697f390 (diff)
parent2de9010f66febb7074706e05ff8d32ec329f4968 (diff)
downloadrust-96be3e7cc8dc1b4f38f66c66f3fcfe35762a48a6.tar.gz
rust-96be3e7cc8dc1b4f38f66c66f3fcfe35762a48a6.zip
Rollup merge of #122784 - jswrenn:tag_for_variant, r=compiler-errors
Add `tag_for_variant` query

This query allows for sharing code between `rustc_const_eval` and `rustc_transmutability`. It's a precursor to a PR I'm working on to entirely replace the bespoke layout computations in `rustc_transmutability`.

r? `@compiler-errors`
Diffstat (limited to 'compiler/rustc_const_eval/src/interpret')
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs156
1 files changed, 89 insertions, 67 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 6d4f6d0cb3c..40469c6632c 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -2,7 +2,7 @@
 
 use rustc_middle::mir;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ScalarInt, Ty};
 use rustc_target::abi::{self, TagEncoding};
 use rustc_target::abi::{VariantIdx, Variants};
 
@@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             throw_ub!(UninhabitedEnumVariantWritten(variant_index))
         }
 
-        match dest.layout().variants {
-            abi::Variants::Single { index } => {
-                assert_eq!(index, variant_index);
-            }
-            abi::Variants::Multiple {
-                tag_encoding: TagEncoding::Direct,
-                tag: tag_layout,
-                tag_field,
-                ..
-            } => {
+        match self.tag_for_variant(dest.layout().ty, variant_index)? {
+            Some((tag, tag_field)) => {
                 // No need to validate that the discriminant here because the
-                // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
-
-                let discr_val = dest
-                    .layout()
-                    .ty
-                    .discriminant_for_variant(*self.tcx, variant_index)
-                    .unwrap()
-                    .val;
-
-                // raw discriminants for enums are isize or bigger during
-                // their computation, but the in-memory tag is the smallest possible
-                // representation
-                let size = tag_layout.size(self);
-                let tag_val = size.truncate(discr_val);
-
+                // `TyAndLayout::for_variant()` call earlier already checks the
+                // variant is valid.
                 let tag_dest = self.project_field(dest, tag_field)?;
-                self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
+                self.write_scalar(tag, &tag_dest)
             }
-            abi::Variants::Multiple {
-                tag_encoding:
-                    TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
-                tag: tag_layout,
-                tag_field,
-                ..
-            } => {
-                // No need to validate that the discriminant here because the
-                // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
-
-                if variant_index != untagged_variant {
-                    let variants_start = niche_variants.start().as_u32();
-                    let variant_index_relative = variant_index
-                        .as_u32()
-                        .checked_sub(variants_start)
-                        .expect("overflow computing relative variant idx");
-                    // We need to use machine arithmetic when taking into account `niche_start`:
-                    // tag_val = variant_index_relative + niche_start_val
-                    let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
-                    let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
-                    let variant_index_relative_val =
-                        ImmTy::from_uint(variant_index_relative, tag_layout);
-                    let tag_val = self.wrapping_binary_op(
-                        mir::BinOp::Add,
-                        &variant_index_relative_val,
-                        &niche_start_val,
-                    )?;
-                    // Write result.
-                    let niche_dest = self.project_field(dest, tag_field)?;
-                    self.write_immediate(*tag_val, &niche_dest)?;
-                } else {
-                    // The untagged variant is implicitly encoded simply by having a value that is
-                    // outside the niche variants. But what if the data stored here does not
-                    // actually encode this variant? That would be bad! So let's double-check...
-                    let actual_variant = self.read_discriminant(&dest.to_op(self)?)?;
-                    if actual_variant != variant_index {
-                        throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty });
-                    }
+            None => {
+                // No need to write the tag here, because an untagged variant is
+                // implicitly encoded. For `Niche`-optimized enums, it's by
+                // simply by having a value that is outside the niche variants.
+                // But what if the data stored here does not actually encode
+                // this variant? That would be bad! So let's double-check...
+                let actual_variant = self.read_discriminant(&dest.to_op(self)?)?;
+                if actual_variant != variant_index {
+                    throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty });
                 }
+                Ok(())
             }
         }
-
-        Ok(())
     }
 
     /// Read discriminant, return the runtime value as well as the variant index.
@@ -277,4 +226,77 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         Ok(ImmTy::from_scalar(discr_value, discr_layout))
     }
+
+    /// Computes the tag value and its field number (if any) of a given variant
+    /// of type `ty`.
+    pub(crate) fn tag_for_variant(
+        &self,
+        ty: Ty<'tcx>,
+        variant_index: VariantIdx,
+    ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
+        match self.layout_of(ty)?.variants {
+            abi::Variants::Single { index } => {
+                assert_eq!(index, variant_index);
+                Ok(None)
+            }
+
+            abi::Variants::Multiple {
+                tag_encoding: TagEncoding::Direct,
+                tag: tag_layout,
+                tag_field,
+                ..
+            } => {
+                // raw discriminants for enums are isize or bigger during
+                // their computation, but the in-memory tag is the smallest possible
+                // representation
+                let discr = self.discriminant_for_variant(ty, variant_index)?;
+                let discr_size = discr.layout.size;
+                let discr_val = discr.to_scalar().to_bits(discr_size)?;
+                let tag_size = tag_layout.size(self);
+                let tag_val = tag_size.truncate(discr_val);
+                let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap();
+                Ok(Some((tag, tag_field)))
+            }
+
+            abi::Variants::Multiple {
+                tag_encoding: TagEncoding::Niche { untagged_variant, .. },
+                ..
+            } if untagged_variant == variant_index => {
+                // The untagged variant is implicitly encoded simply by having a
+                // value that is outside the niche variants.
+                Ok(None)
+            }
+
+            abi::Variants::Multiple {
+                tag_encoding:
+                    TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
+                tag: tag_layout,
+                tag_field,
+                ..
+            } => {
+                assert!(variant_index != untagged_variant);
+                let variants_start = niche_variants.start().as_u32();
+                let variant_index_relative = variant_index
+                    .as_u32()
+                    .checked_sub(variants_start)
+                    .expect("overflow computing relative variant idx");
+                // We need to use machine arithmetic when taking into account `niche_start`:
+                // tag_val = variant_index_relative + niche_start_val
+                let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
+                let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
+                let variant_index_relative_val =
+                    ImmTy::from_uint(variant_index_relative, tag_layout);
+                let tag = self
+                    .wrapping_binary_op(
+                        mir::BinOp::Add,
+                        &variant_index_relative_val,
+                        &niche_start_val,
+                    )?
+                    .to_scalar()
+                    .try_to_int()
+                    .unwrap();
+                Ok(Some((tag, tag_field)))
+            }
+        }
+    }
 }