about summary refs log tree commit diff
path: root/compiler/rustc_const_eval/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_const_eval/src')
-rw-r--r--compiler/rustc_const_eval/src/errors.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs79
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs30
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs64
5 files changed, 148 insertions, 37 deletions
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 9133a5fc8ef..49cd7138748 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -475,6 +475,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             WriteToReadOnly(_) => const_eval_write_to_read_only,
             DerefFunctionPointer(_) => const_eval_deref_function_pointer,
             DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
+            DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer,
             InvalidBool(_) => const_eval_invalid_bool,
             InvalidChar(_) => const_eval_invalid_char,
             InvalidTag(_) => const_eval_invalid_tag,
@@ -588,7 +589,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
                 diag.arg("has", has.bytes());
                 diag.arg("msg", format!("{msg:?}"));
             }
-            WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
+            WriteToReadOnly(alloc)
+            | DerefFunctionPointer(alloc)
+            | DerefVTablePointer(alloc)
+            | DerefTypeIdPointer(alloc) => {
                 diag.arg("allocation", alloc);
             }
             InvalidBool(b) => {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 378ed6d0e10..ee5382af0b2 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -4,8 +4,10 @@
 
 use std::assert_matches::assert_matches;
 
-use rustc_abi::Size;
+use rustc_abi::{FieldIdx, Size};
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
+use rustc_ast::Mutability;
+use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range};
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -29,6 +31,37 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
     tcx.mk_const_alloc(alloc)
 }
 
+pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId {
+    let size = Size::from_bytes(16);
+    let align = tcx.data_layout.pointer_align();
+    let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ());
+    let ptr_size = tcx.data_layout.pointer_size();
+    let type_id_hash = tcx.type_id_hash(ty).as_u128();
+    alloc
+        .write_scalar(
+            &tcx,
+            alloc_range(Size::ZERO, Size::from_bytes(16)),
+            Scalar::from_u128(type_id_hash),
+        )
+        .unwrap();
+
+    // Give the first pointer-size bytes provenance that knows about the type id
+
+    let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
+    let offset = alloc
+        .read_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), false)
+        .unwrap()
+        .to_target_usize(&tcx)
+        .unwrap();
+    let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
+    let val = Scalar::from_pointer(ptr, &tcx);
+    alloc.write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), val).unwrap();
+
+    alloc.mutability = Mutability::Not;
+
+    tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc))
+}
+
 impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Returns `true` if emulation happened.
     /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
@@ -63,10 +96,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             sym::type_id => {
                 let tp_ty = instance.args.type_at(0);
                 ensure_monomorphic_enough(tcx, tp_ty)?;
-                let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128());
+                let alloc_id = alloc_type_id(tcx, tp_ty);
+                let val = ConstValue::Indirect { alloc_id, offset: Size::ZERO };
                 let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
                 self.copy_op(&val, dest)?;
             }
+            sym::type_id_eq => {
+                // Both operands are `TypeId`, which is a newtype around an array of pointers.
+                // Project until we have the array elements.
+                let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
+                let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
+
+                let mut a_fields = self.project_array_fields(&a_fields)?;
+                let mut b_fields = self.project_array_fields(&b_fields)?;
+
+                let (_idx, a) = a_fields
+                    .next(self)?
+                    .expect("we know the layout of TypeId has at least 2 array elements");
+                let a = self.deref_pointer(&a)?;
+                let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
+
+                let (_idx, b) = b_fields
+                    .next(self)?
+                    .expect("we know the layout of TypeId has at least 2 array elements");
+                let b = self.deref_pointer(&b)?;
+                let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
+
+                let provenance_matches = a == b;
+
+                let mut eq_id = offset_a == offset_b;
+
+                while let Some((_, a)) = a_fields.next(self)? {
+                    let (_, b) = b_fields.next(self)?.unwrap();
+
+                    let a = self.read_target_usize(&a)?;
+                    let b = self.read_target_usize(&b)?;
+                    eq_id &= a == b;
+                }
+
+                if !eq_id && provenance_matches {
+                    throw_ub_format!(
+                        "type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
+                    )
+                }
+
+                self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
+            }
             sym::variant_count => {
                 let tp_ty = instance.args.type_at(0);
                 let ty = match tp_ty.kind() {
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 524023b8104..bb301600135 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -15,9 +15,9 @@ use std::{fmt, ptr};
 use rustc_abi::{Align, HasDataLayout, Size};
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
-use rustc_middle::bug;
 use rustc_middle::mir::display_allocation;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::{bug, throw_ub_format};
 use tracing::{debug, instrument, trace};
 
 use super::{
@@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         kind = "vtable",
                     )
                 }
+                Some(GlobalAlloc::TypeId { .. }) => {
+                    err_ub_custom!(
+                        fluent::const_eval_invalid_dealloc,
+                        alloc_id = alloc_id,
+                        kind = "typeid",
+                    )
+                }
                 Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
                     err_ub_custom!(
                         fluent::const_eval_invalid_dealloc,
@@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
             Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
+            Some(GlobalAlloc::TypeId { .. }) => throw_ub!(DerefTypeIdPointer(id)),
             None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
             Some(GlobalAlloc::Static(def_id)) => {
                 assert!(self.tcx.is_static(def_id));
@@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
             let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
             let kind = match global_alloc {
-                GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
+                GlobalAlloc::TypeId { .. }
+                | GlobalAlloc::Static { .. }
+                | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
                 GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
                 GlobalAlloc::VTable { .. } => AllocKind::VTable,
             };
@@ -936,6 +946,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         }
     }
 
+    /// Takes a pointer that is the first chunk of a `TypeId` and return the type that its
+    /// provenance refers to, as well as the segment of the hash that this pointer covers.
+    pub fn get_ptr_type_id(
+        &self,
+        ptr: Pointer<Option<M::Provenance>>,
+    ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
+        let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
+        let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
+            throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
+        };
+        interp_ok((ty, offset))
+    }
+
     pub fn get_ptr_fn(
         &self,
         ptr: Pointer<Option<M::Provenance>>,
@@ -1197,6 +1220,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
                         Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
                             write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?;
                         }
+                        Some(GlobalAlloc::TypeId { ty }) => {
+                            write!(fmt, " (typeid for {ty})")?;
+                        }
                         Some(GlobalAlloc::Static(did)) => {
                             write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
                         }
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 306697d4ec9..f72c4418081 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -296,7 +296,11 @@ where
         base: &'a P,
     ) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> {
         let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
-            span_bug!(self.cur_span(), "project_array_fields: expected an array layout");
+            span_bug!(
+                self.cur_span(),
+                "project_array_fields: expected an array layout, got {:#?}",
+                base.layout()
+            );
         };
         let len = base.len(self)?;
         let field_layout = base.layout().field(self, 0);
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index fc4d13af8c4..fc44490c96d 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -571,40 +571,42 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     let alloc_actual_mutbl =
                         global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
 
-                    if let GlobalAlloc::Static(did) = global_alloc {
-                        let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
-                            bug!()
-                        };
-                        // Special handling for pointers to statics (irrespective of their type).
-                        assert!(!self.ecx.tcx.is_thread_local_static(did));
-                        assert!(self.ecx.tcx.is_static(did));
-                        // Mode-specific checks
-                        match ctfe_mode {
-                            CtfeValidationMode::Static { .. }
-                            | CtfeValidationMode::Promoted { .. } => {
-                                // We skip recursively checking other statics. These statics must be sound by
-                                // themselves, and the only way to get broken statics here is by using
-                                // unsafe code.
-                                // The reasons we don't check other statics is twofold. For one, in all
-                                // sound cases, the static was already validated on its own, and second, we
-                                // trigger cycle errors if we try to compute the value of the other static
-                                // and that static refers back to us (potentially through a promoted).
-                                // This could miss some UB, but that's fine.
-                                // We still walk nested allocations, as they are fundamentally part of this validation run.
-                                // This means we will also recurse into nested statics of *other*
-                                // statics, even though we do not recurse into other statics directly.
-                                // That's somewhat inconsistent but harmless.
-                                skip_recursive_check = !nested;
-                            }
-                            CtfeValidationMode::Const { .. } => {
-                                // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
-                                // just get errors trying to read the value.
-                                if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did)
-                                {
-                                    skip_recursive_check = true;
+                    match global_alloc {
+                        GlobalAlloc::Static(did) => {
+                            let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
+                                bug!()
+                            };
+                            assert!(!self.ecx.tcx.is_thread_local_static(did));
+                            assert!(self.ecx.tcx.is_static(did));
+                            match ctfe_mode {
+                                CtfeValidationMode::Static { .. }
+                                | CtfeValidationMode::Promoted { .. } => {
+                                    // We skip recursively checking other statics. These statics must be sound by
+                                    // themselves, and the only way to get broken statics here is by using
+                                    // unsafe code.
+                                    // The reasons we don't check other statics is twofold. For one, in all
+                                    // sound cases, the static was already validated on its own, and second, we
+                                    // trigger cycle errors if we try to compute the value of the other static
+                                    // and that static refers back to us (potentially through a promoted).
+                                    // This could miss some UB, but that's fine.
+                                    // We still walk nested allocations, as they are fundamentally part of this validation run.
+                                    // This means we will also recurse into nested statics of *other*
+                                    // statics, even though we do not recurse into other statics directly.
+                                    // That's somewhat inconsistent but harmless.
+                                    skip_recursive_check = !nested;
+                                }
+                                CtfeValidationMode::Const { .. } => {
+                                    // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
+                                    // just get errors trying to read the value.
+                                    if alloc_actual_mutbl.is_mut()
+                                        || self.ecx.tcx.is_foreign_item(did)
+                                    {
+                                        skip_recursive_check = true;
+                                    }
                                 }
                             }
                         }
+                        _ => (),
                     }
 
                     // If this allocation has size zero, there is no actual mutability here.