From ae67462a3764a46b0cb770256985dd6b4a2fe198 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 21 Jun 2025 11:26:27 +0000 Subject: [PATCH 01/12] Stop backends from needing to support nullary intrinsics --- .../example/mini_core.rs | 2 +- .../example/mini_core_hello_world.rs | 23 +++++-- .../src/intrinsics/mod.rs | 15 ----- .../example/mini_core_hello_world.rs | 9 +-- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 4 -- library/core/src/any.rs | 4 +- library/core/src/intrinsics/mod.rs | 32 +++++----- library/core/src/mem/mod.rs | 4 +- .../stable-mir/check_allocation.rs | 60 +++++++------------ 9 files changed, 67 insertions(+), 86 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 524ebde1c743f..2f53bbf8b7934 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -660,7 +660,7 @@ pub mod intrinsics { #[rustc_intrinsic] pub unsafe fn ctlz_nonzero(x: T) -> u32; #[rustc_intrinsic] - pub fn needs_drop() -> bool; + pub const fn needs_drop() -> bool; #[rustc_intrinsic] pub fn bitreverse(x: T) -> T; #[rustc_intrinsic] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 1499f948deb3c..246bd3104ec41 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,13 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] +#![feature( + no_core, + lang_items, + never_type, + linkage, + extern_types, + thread_local, + repr_simd, + rustc_private +)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -207,10 +216,14 @@ fn main() { assert_eq!(intrinsics::align_of::() as u8, 2); assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); - assert!(!intrinsics::needs_drop::()); - assert!(!intrinsics::needs_drop::<[u8]>()); - assert!(intrinsics::needs_drop::()); - assert!(intrinsics::needs_drop::()); + let u8_needs_drop = const { intrinsics::needs_drop::() }; + assert!(!u8_needs_drop); + let slice_needs_drop = const { intrinsics::needs_drop::<[u8]>() }; + assert!(!slice_needs_drop); + let noisy_drop = const { intrinsics::needs_drop::() }; + assert!(noisy_drop); + let noisy_unsized_drop = const { intrinsics::needs_drop::() }; + assert!(noisy_unsized_drop); Unique { pointer: NonNull(1 as *mut &str), _marker: PhantomData } as Unique; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index df5748c34d110..4ff5773a06cb2 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -812,21 +812,6 @@ fn codegen_regular_intrinsic_call<'tcx>( dest.write_cvalue(fx, val); } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - intrinsic_args!(fx, args => (); intrinsic); - - let const_val = fx - .tcx - .const_eval_instance( - ty::TypingEnv::fully_monomorphized(), - instance, - source_info.span, - ) - .unwrap(); - let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); - ret.write_cvalue(fx, val); - } - sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { intrinsic_args!(fx, args => (ptr, base); intrinsic); let ptr = ptr.load_scalar(fx); diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index c3bd62e5897cc..6b6f71edaf8c9 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -6,6 +6,7 @@ )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] +#![rustfmt::skip] extern crate mini_core; @@ -197,10 +198,10 @@ fn main() { assert_eq!(intrinsics::align_of::() as u8, 2); assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); - assert!(!intrinsics::needs_drop::()); - assert!(!intrinsics::needs_drop::<[u8]>()); - assert!(intrinsics::needs_drop::()); - assert!(intrinsics::needs_drop::()); + assert!(!const { intrinsics::needs_drop::() }); + assert!(!const { intrinsics::needs_drop::<[u8]>() }); + assert!(const { intrinsics::needs_drop::() }); + assert!(const { intrinsics::needs_drop::() }); Unique { pointer: 0 as *const &str, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 27fcab8ed2d6c..f77c8e500cc18 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -150,10 +150,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } value } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap(); - OperandRef::from_const(bx, value, result.layout.ty).immediate_or_packed_pair(bx) - } sym::arith_offset => { let ty = fn_args.type_at(0); let layout = bx.layout_of(ty); diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7aa3f3c6d7434..01dce114592a1 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -742,7 +742,7 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - let t: u128 = intrinsics::type_id::(); + let t: u128 = const { intrinsics::type_id::() }; let t1 = (t >> 64) as u64; let t2 = t as u64; @@ -824,7 +824,7 @@ impl fmt::Debug for TypeId { #[stable(feature = "type_name", since = "1.38.0")] #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub const fn type_name() -> &'static str { - intrinsics::type_name::() + const { intrinsics::type_name::() } } /// Returns the type name of the pointed-to value as a string slice. diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index b5c3e91d04687..47dd7c7aefd73 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -839,10 +839,10 @@ pub const unsafe fn transmute_unchecked(src: Src) -> Dst; /// If the actual type neither requires drop glue nor implements /// `Copy`, then the return value of this function is unspecified. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). #[rustc_intrinsic_const_stable_indirect] @@ -2655,10 +2655,10 @@ pub const fn align_of() -> usize; /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. #[rustc_nounwind] @@ -2694,10 +2694,10 @@ pub const unsafe fn align_of_val(ptr: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_nounwind] @@ -2709,10 +2709,10 @@ pub const fn type_name() -> &'static str; /// function will return the same value for a type regardless of whichever /// crate it is invoked in. /// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. +/// Note that, unlike most intrinsics, this can only be called at compile-time +/// as backends do not have an implementation for it. The only caller (its +/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_nounwind] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index b93f854b9dc43..c00585de06496 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -616,7 +616,7 @@ pub const unsafe fn align_of_val_raw(val: *const T) -> usize { #[rustc_const_stable(feature = "const_mem_needs_drop", since = "1.36.0")] #[rustc_diagnostic_item = "needs_drop"] pub const fn needs_drop() -> bool { - intrinsics::needs_drop::() + const { intrinsics::needs_drop::() } } /// Returns the value of type `T` represented by the all-zero byte-pattern. @@ -1215,7 +1215,7 @@ pub const fn discriminant(v: &T) -> Discriminant { #[rustc_const_unstable(feature = "variant_count", issue = "73662")] #[rustc_diagnostic_item = "mem_variant_count"] pub const fn variant_count() -> usize { - intrinsics::variant_count::() + const { intrinsics::variant_count::() } } /// Provides associated constants for various useful properties of types, diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs index 692c24f054451..64194e728886a 100644 --- a/tests/ui-fulldeps/stable-mir/check_allocation.rs +++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs @@ -19,12 +19,6 @@ extern crate rustc_driver; extern crate rustc_interface; extern crate stable_mir; -use stable_mir::crate_def::CrateDef; -use stable_mir::mir::alloc::GlobalAlloc; -use stable_mir::mir::mono::{Instance, InstanceKind, StaticDef}; -use stable_mir::mir::{Body, TerminatorKind}; -use stable_mir::ty::{Allocation, ConstantKind, RigidTy, TyKind}; -use stable_mir::{CrateItem, CrateItems, ItemKind}; use std::ascii::Char; use std::assert_matches::assert_matches; use std::cmp::{max, min}; @@ -33,6 +27,13 @@ use std::ffi::CStr; use std::io::Write; use std::ops::ControlFlow; +use stable_mir::crate_def::CrateDef; +use stable_mir::mir::Body; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::mir::mono::{Instance, StaticDef}; +use stable_mir::ty::{Allocation, ConstantKind}; +use stable_mir::{CrateItem, CrateItems, ItemKind}; + const CRATE_NAME: &str = "input"; /// This function uses the Stable MIR APIs to get information about the test crate. @@ -44,7 +45,6 @@ fn test_stable_mir() -> ControlFlow<()> { check_len(*get_item(&items, (ItemKind::Static, "LEN")).unwrap()); check_cstr(*get_item(&items, (ItemKind::Static, "C_STR")).unwrap()); check_other_consts(*get_item(&items, (ItemKind::Fn, "other_consts")).unwrap()); - check_type_id(*get_item(&items, (ItemKind::Fn, "check_type_id")).unwrap()); ControlFlow::Continue(()) } @@ -107,7 +107,9 @@ fn check_other_consts(item: CrateItem) { // Instance body will force constant evaluation. let body = Instance::try_from(item).unwrap().body().unwrap(); let assigns = collect_consts(&body); - assert_eq!(assigns.len(), 8); + assert_eq!(assigns.len(), 10); + let mut char_id = None; + let mut bool_id = None; for (name, alloc) in assigns { match name.as_str() { "_max_u128" => { @@ -149,35 +151,21 @@ fn check_other_consts(item: CrateItem) { assert_eq!(max(first, second) as u32, u32::MAX); assert_eq!(min(first, second), 10); } + "_bool_id" => { + bool_id = Some(alloc); + } + "_char_id" => { + char_id = Some(alloc); + } _ => { unreachable!("{name} -- {alloc:?}") } } } -} - -/// Check that we can retrieve the type id of char and bool, and that they have different values. -fn check_type_id(item: CrateItem) { - let body = Instance::try_from(item).unwrap().body().unwrap(); - let mut ids: Vec = vec![]; - for term in body.blocks.iter().map(|bb| &bb.terminator) { - match &term.kind { - TerminatorKind::Call { func, destination, .. } => { - let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { - unreachable!() - }; - let RigidTy::FnDef(def, args) = ty else { unreachable!() }; - let instance = Instance::resolve(def, &args).unwrap(); - assert_eq!(instance.kind, InstanceKind::Intrinsic); - let dest_ty = destination.ty(body.locals()).unwrap(); - let alloc = instance.try_const_eval(dest_ty).unwrap(); - ids.push(alloc.read_uint().unwrap()); - } - _ => { /* Do nothing */ } - } - } - assert_eq!(ids.len(), 2); - assert_ne!(ids[0], ids[1]); + let bool_id = bool_id.unwrap(); + let char_id = char_id.unwrap(); + // FIXME(stable_mir): add `read_ptr` to `Allocation` + assert_ne!(bool_id, char_id); } /// Collects all the constant assignments. @@ -235,6 +223,7 @@ fn generate_input(path: &str) -> std::io::Result<()> { file, r#" #![feature(core_intrinsics)] + #![expect(internal_features)] use std::intrinsics::type_id; static LEN: usize = 2; @@ -254,11 +243,8 @@ fn generate_input(path: &str) -> std::io::Result<()> { let _ptr = &BAR; let _null_ptr: *const u8 = NULL; let _tuple = TUPLE; - }} - - fn check_type_id() {{ - let _char_id = type_id::(); - let _bool_id = type_id::(); + let _char_id = const {{ type_id::() }}; + let _bool_id = const {{ type_id::() }}; }} pub fn main() {{ From 293a634e5b49146603d6601c615f81a4d851412a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 21 Jun 2025 17:33:55 +0000 Subject: [PATCH 02/12] Remove the nullary intrinsic const eval logic and treat them like other intrinsics --- compiler/rustc_const_eval/messages.ftl | 3 - .../src/const_eval/eval_queries.rs | 30 +--- .../src/interpret/intrinsics.rs | 158 ++++++++---------- .../rustc_const_eval/src/interpret/mod.rs | 1 - library/core/src/intrinsics/mod.rs | 8 +- tests/ui/consts/const-fn-type-name.rs | 2 +- 6 files changed, 78 insertions(+), 124 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 97b154ad14235..b754087108a17 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -271,9 +271,6 @@ const_eval_non_const_try_block_from_output = const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires -const_eval_nullary_intrinsic_fail = - could not evaluate nullary intrinsic - const_eval_offset_from_different_allocations = `{$name}` called on two different pointers that are not both derived from the same allocation const_eval_offset_from_out_of_bounds = diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index be84019154710..ffe22c58082c0 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -20,7 +20,7 @@ use crate::const_eval::CheckAlignment; use crate::interpret::{ CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, - eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, + intern_const_alloc_recursive, interp_ok, throw_exhaust, }; use crate::{CTRL_C_RECEIVED, errors}; @@ -280,34 +280,6 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - // We call `const_eval` for zero arg intrinsics, too, in order to cache their value. - // Catch such calls and evaluate them instead of trying to load a constant's MIR. - if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def { - let ty = key.value.instance.ty(tcx, key.typing_env); - let ty::FnDef(_, args) = ty.kind() else { - bug!("intrinsic with type {:?}", ty); - }; - return eval_nullary_intrinsic(tcx, key.typing_env, def_id, args).report_err().map_err( - |error| { - let span = tcx.def_span(def_id); - - // FIXME(oli-obk): why don't we have any tests for this code path? - super::report( - tcx, - error.into_kind(), - span, - || (span, vec![]), - |diag, span, _| { - diag.span_label( - span, - crate::fluent_generated::const_eval_nullary_intrinsic_fail, - ); - }, - ) - }, - ); - } - tcx.eval_to_allocation_raw(key).map(|val| turn_into_const_value(tcx, val, key)) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 96c39c7bb32ba..1b56d70865866 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,10 +6,9 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; -use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement}; -use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -17,8 +16,8 @@ use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ - Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, Machine, - OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, + Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy, + PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -30,73 +29,6 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll tcx.mk_const_alloc(alloc) } -/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated -/// inside an `InterpCx` and instead have their value computed directly from rustc internal info. -pub(crate) fn eval_nullary_intrinsic<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - def_id: DefId, - args: GenericArgsRef<'tcx>, -) -> InterpResult<'tcx, ConstValue<'tcx>> { - let tp_ty = args.type_at(0); - let name = tcx.item_name(def_id); - interp_ok(match name { - sym::type_name => { - ensure_monomorphic_enough(tcx, tp_ty)?; - let alloc = alloc_type_name(tcx, tp_ty); - ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() } - } - sym::needs_drop => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_bool(tp_ty.needs_drop(tcx, typing_env)) - } - sym::type_id => { - ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) - } - sym::variant_count => match match tp_ty.kind() { - // Pattern types have the same number of variants as their base type. - // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. - // And `Result<(), !>` still has two variants according to `variant_count`. - ty::Pat(base, _) => *base, - _ => tp_ty, - } - .kind() - { - // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. - ty::Adt(adt, _) => ConstValue::from_target_usize(adt.variants().len() as u64, &tcx), - ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { - throw_inval!(TooGeneric) - } - ty::Pat(..) => unreachable!(), - ty::Bound(_, _) => bug!("bound ty during ctfe"), - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _, _) - | ty::Closure(_, _) - | ty::CoroutineClosure(_, _) - | ty::Coroutine(_, _) - | ty::CoroutineWitness(..) - | ty::UnsafeBinder(_) - | ty::Never - | ty::Tuple(_) - | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), - }, - other => bug!("`{}` is not a zero arg intrinsic", other), - }) -} - 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 @@ -110,8 +42,77 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; let intrinsic_name = self.tcx.item_name(instance.def_id()); + let tcx = self.tcx.tcx; match intrinsic_name { + sym::type_name => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let alloc = alloc_type_name(tcx, tp_ty); + let val = ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::needs_drop => { + let tp_ty = instance.args.type_at(0); + ensure_monomorphic_enough(tcx, tp_ty)?; + let val = ConstValue::from_bool(tp_ty.needs_drop(tcx, self.typing_env)); + let val = self.const_val_to_op(val, tcx.types.bool, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + 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 val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::variant_count => { + let tp_ty = instance.args.type_at(0); + let ty = match tp_ty.kind() { + // Pattern types have the same number of variants as their base type. + // Even if we restrict e.g. which variants are valid, the variants are essentially just uninhabited. + // And `Result<(), !>` still has two variants according to `variant_count`. + ty::Pat(base, _) => *base, + _ => tp_ty, + }; + let val = match ty.kind() { + // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. + ty::Adt(adt, _) => { + ConstValue::from_target_usize(adt.variants().len() as u64, &tcx) + } + ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { + throw_inval!(TooGeneric) + } + ty::Pat(..) => unreachable!(), + ty::Bound(_, _) => bug!("bound ty during ctfe"), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::UnsafeBinder(_) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => ConstValue::from_target_usize(0u64, &tcx), + }; + let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; + self.copy_op(&val, dest)?; + } + sym::caller_location => { let span = self.find_closest_untracked_caller_location(); let val = self.tcx.span_as_caller_location(span); @@ -137,21 +138,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(result, self), dest)?; } - sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => { - let gid = GlobalId { instance, promoted: None }; - let ty = self - .tcx - .fn_sig(instance.def_id()) - .instantiate(self.tcx.tcx, instance.args) - .output() - .no_bound_vars() - .unwrap(); - let val = self - .ctfe_query(|tcx| tcx.const_eval_global_id(self.typing_env, gid, tcx.span))?; - let val = self.const_val_to_op(val, ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; - } - sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index f5792aba2075c..f8b3c92debbcd 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -29,7 +29,6 @@ pub use self::intern::{ HasStaticRootDefId, InternKind, InternResult, intern_const_alloc_for_constprop, intern_const_alloc_recursive, }; -pub(crate) use self::intrinsics::eval_nullary_intrinsic; pub use self::machine::{AllocMap, Machine, MayLeak, ReturnAction, compile_time_machine}; pub use self::memory::{AllocInfo, AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; use self::operand::Operand; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 47dd7c7aefd73..4250de9fb2b33 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -841,7 +841,7 @@ pub const unsafe fn transmute_unchecked(src: Src) -> Dst; /// /// Note that, unlike most intrinsics, this can only be called at compile-time /// as backends do not have an implementation for it. The only caller (its -/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// stable counterpart) wraps this intrinsic call in a `const` block so that /// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). @@ -2657,7 +2657,7 @@ pub const fn align_of() -> usize; /// /// Note that, unlike most intrinsics, this can only be called at compile-time /// as backends do not have an implementation for it. The only caller (its -/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// stable counterpart) wraps this intrinsic call in a `const` block so that /// backends only see an evaluated constant. /// /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. @@ -2696,7 +2696,7 @@ pub const unsafe fn align_of_val(ptr: *const T) -> usize; /// /// Note that, unlike most intrinsics, this can only be called at compile-time /// as backends do not have an implementation for it. The only caller (its -/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// stable counterpart) wraps this intrinsic call in a `const` block so that /// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::type_name`]. @@ -2711,7 +2711,7 @@ pub const fn type_name() -> &'static str; /// /// Note that, unlike most intrinsics, this can only be called at compile-time /// as backends do not have an implementation for it. The only caller (its -/// stable counterpart), wraps this intrinsic call in a `const` block so that +/// stable counterpart) wraps this intrinsic call in a `const` block so that /// backends only see an evaluated constant. /// /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. diff --git a/tests/ui/consts/const-fn-type-name.rs b/tests/ui/consts/const-fn-type-name.rs index 5403c26b979ab..733ab79b7cdb8 100644 --- a/tests/ui/consts/const-fn-type-name.rs +++ b/tests/ui/consts/const-fn-type-name.rs @@ -5,7 +5,7 @@ #![allow(dead_code)] const fn type_name_wrapper(_: &T) -> &'static str { - core::intrinsics::type_name::() + const { core::intrinsics::type_name::() } } struct Struct { From b144fa6124f84fcfd54bd7c8a180a5e8c11c7cca Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 26 Jun 2025 14:53:36 +0000 Subject: [PATCH 03/12] Require either wrapping nullary intrinsincs in const blocks or explicitly deciding not to --- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f77c8e500cc18..fc95f62b4a43d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,7 +1,7 @@ use rustc_abi::WrappingRange; -use rustc_middle::bug; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::sym; @@ -98,6 +98,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { discr.to_atomic_ordering() }; + if args.is_empty() { + match name { + sym::abort + | sym::unreachable + | sym::cold_path + | sym::breakpoint + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid + | sym::assert_inhabited + | sym::ub_checks + | sym::contract_checks + | sym::atomic_fence + | sym::atomic_singlethreadfence + | sym::caller_location => {} + _ => { + span_bug!(span, "nullary intrinsic {name} must either be in a const block or explicitly opted out because it is inherently a runtime intrinsic +"); + } + } + } + let llval = match name { sym::abort => { bx.abort(); From 4698db1c4c7cb73bcb2bfc92244c043e8c31f6be Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 27 Jun 2025 10:30:12 +0000 Subject: [PATCH 04/12] Try increasing the repetition limit --- src/tools/miri/tests/pass/float.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 383579198bbe5..6b782f55359ac 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1358,7 +1358,7 @@ fn test_min_max_nondet() { /// Ensure that if we call the closure often enough, we see both `true` and `false.` #[track_caller] fn ensure_both(f: impl Fn() -> bool) { - let rounds = 16; + let rounds = 32; let first = f(); for _ in 1..rounds { if f() != first { From ff19605705b18d5159382eaa267a73c9aa2e7984 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 12 Mar 2025 10:26:37 +0000 Subject: [PATCH 05/12] Add opaque TypeId handles for CTFE --- .../rustc_codegen_cranelift/src/constant.rs | 5 +++ compiler/rustc_codegen_gcc/src/common.rs | 2 ++ compiler/rustc_codegen_llvm/src/common.rs | 32 ++++++++++++++---- compiler/rustc_codegen_llvm/src/consts.rs | 5 ++- compiler/rustc_const_eval/messages.ftl | 2 ++ compiler/rustc_const_eval/src/errors.rs | 6 +++- .../rustc_const_eval/src/interpret/memory.rs | 15 ++++++++- .../rustc_middle/src/mir/interpret/error.rs | 2 ++ .../rustc_middle/src/mir/interpret/mod.rs | 33 ++++++++++++++++--- compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 1 + compiler/rustc_passes/src/reachable.rs | 1 + .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + 14 files changed, 91 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 3a62cd52a9d94..07c58eb05d166 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -175,6 +175,8 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } + // TODO: generate pointer to allocation containing the actual type id hash u128 value + GlobalAlloc::Type(_) => todo!(), GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static( @@ -360,6 +362,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(alloc) => alloc, GlobalAlloc::Function { .. } | GlobalAlloc::Static(_) + | GlobalAlloc::Type(_) | GlobalAlloc::VTable(..) => { unreachable!() } @@ -471,6 +474,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant .principal() .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), ), + // TODO + GlobalAlloc::Type(_ty) => todo!(), GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 58ff2f1f8f064..44f484b6e633e 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -303,6 +303,8 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { let init = self.const_data_from_alloc(alloc); self.static_addr_of(init, alloc.inner().align, None) } + // TODO: generate pointer to allocation containing the actual type id hash u128 value + GlobalAlloc::Type(_ty) => todo!(), GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index ae5add59322fe..aa47bc7b61fc3 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -3,9 +3,8 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -13,7 +12,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hashes::Hash128; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; +use rustc_middle::mir::interpret::{ + AllocInit, Allocation, ConstAllocation, GlobalAlloc, Scalar, alloc_range, +}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::DllImport; use tracing::debug; @@ -284,7 +285,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); + let init = + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -316,7 +318,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); + let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); let value = self.static_addr_of_impl(init, alloc.inner().align, None); value } @@ -325,6 +327,24 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } + GlobalAlloc::Type(ty) => { + let type_id = self.tcx.type_id_hash(ty).as_u128(); + let mut alloc = Allocation::new( + Size::from_bytes(16), + Align::from_bytes(8).unwrap(), + AllocInit::Uninit, + (), + ); + alloc + .write_scalar( + &self.tcx, + alloc_range(Size::ZERO, Size::from_bytes(16)), + Scalar::from_u128(type_id), + ) + .unwrap(); + let init = const_alloc_to_llvm(self, &alloc, /*static*/ false); + self.static_addr_of_impl(init, alloc.align, None) + } }; let base_addr_space = global_alloc.address_space(self); let llval = unsafe { @@ -346,7 +366,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { } fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { - const_alloc_to_llvm(self, alloc, /*static*/ false) + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false) } fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a4492d76c3c59..74a4999d5097b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -27,10 +27,9 @@ use crate::{base, debuginfo}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, - alloc: ConstAllocation<'_>, + alloc: &Allocation, is_static: bool, ) -> &'ll Value { - let alloc = alloc.inner(); // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be // producing empty LLVM allocations as they're just adding noise to binaries and forcing less @@ -138,7 +137,7 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc)) + Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index b754087108a17..a30018cfb3fb8 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -78,6 +78,8 @@ const_eval_dealloc_kind_mismatch = const_eval_deref_function_pointer = accessing {$allocation} which contains a function +const_eval_deref_typeid_pointer = + accessing {$allocation} which contains a `TypeId` const_eval_deref_vtable_pointer = accessing {$allocation} which contains a vtable const_eval_division_by_zero = diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b2c3103c34af6..28c248e6ab8d7 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -474,6 +474,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, @@ -587,7 +588,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/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 57bf867e389b2..fc610bb7fedec 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "vtable", ) } + Some(GlobalAlloc::Type(..)) => { + 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::Type(..)) => throw_ub!(DerefTypeIdPointer(id)), None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -891,7 +899,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::Type(_) | GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => { + AllocKind::LiveData + } GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), GlobalAlloc::VTable { .. } => AllocKind::VTable, }; @@ -1201,6 +1211,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::Type(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_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 41a166083d079..dc8985d1b962c 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { DerefFunctionPointer(AllocId), /// Trying to access the data behind a vtable pointer. DerefVTablePointer(AllocId), + /// Trying to access the actual type id. + DerefTypeIdPointer(AllocId), /// Using a non-boolean `u8` as bool. InvalidBool(u8), /// Using a non-character `u32` as character. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index ea2f84d46d7f7..563970830ecde 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -103,6 +103,7 @@ enum AllocDiscriminant { Fn, VTable, Static, + Type, } pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( @@ -127,6 +128,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( ty.encode(encoder); poly_trait_ref.encode(encoder); } + GlobalAlloc::Type(ty) => { + trace!("encoding {alloc_id:?} with {ty:#?}"); + AllocDiscriminant::Type.encode(encoder); + ty.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -228,6 +234,12 @@ impl<'s> AllocDecodingSession<'s> { trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT) } + AllocDiscriminant::Type => { + trace!("creating typeid alloc ID"); + let ty = Decodable::decode(decoder); + trace!("decoded typid: {ty:?}"); + decoder.interner().reserve_and_set_type_id_alloc(ty) + } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); let did = >::decode(decoder); @@ -258,6 +270,9 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), + /// A TypeId pointer. For now cannot be turned into a runtime value. + /// TODO: turn into actual TypeId? + Type(Ty<'tcx>), } impl<'tcx> GlobalAlloc<'tcx> { @@ -296,9 +311,10 @@ impl<'tcx> GlobalAlloc<'tcx> { pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { match self { GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, - GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { - AddressSpace::DATA - } + GlobalAlloc::Type(_) + | GlobalAlloc::Static(..) + | GlobalAlloc::Memory(..) + | GlobalAlloc::VTable(..) => AddressSpace::DATA, } } @@ -334,7 +350,7 @@ impl<'tcx> GlobalAlloc<'tcx> { } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { + GlobalAlloc::Type(_) | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { // These are immutable. Mutability::Not } @@ -380,8 +396,10 @@ impl<'tcx> GlobalAlloc<'tcx> { GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE), GlobalAlloc::VTable(..) => { // No data to be accessed here. But vtables are pointer-aligned. - return (Size::ZERO, tcx.data_layout.pointer_align.abi); + (Size::ZERO, tcx.data_layout.pointer_align.abi) } + // TODO make this represent normal type ids somehow + GlobalAlloc::Type(_) => (Size::from_bytes(16), Align::from_bytes(8).unwrap()), } } } @@ -487,6 +505,11 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } + /// Generates an [AllocId] for a [core::mem::type_info::TypeId]. Will get deduplicated. + pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Type(ty), 0) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 6b262a2750059..6250e501f9973 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1621,6 +1621,7 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(w, " (vtable: impl {dyn_ty} for {ty})")? } + Some(GlobalAlloc::Type(ty)) => write!(w, " (typeid for {ty})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c10277c75a79b..87d10a86ac427 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1780,6 +1780,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } Some(GlobalAlloc::Function { .. }) => p!(""), Some(GlobalAlloc::VTable(..)) => p!(""), + Some(GlobalAlloc::Type(_)) => p!(""), None => p!(""), } return Ok(()); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index e90e32ebebb9f..75e94deabca58 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1227,6 +1227,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt )); collect_alloc(tcx, alloc_id, output) } + GlobalAlloc::Type(_) => {} } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 7e15267a953ba..ed3260503fdcd 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -325,6 +325,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(args); } } + GlobalAlloc::Type(ty) => self.visit(ty), GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 42b3e59b73ab9..9f4c8f4de58c9 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -752,6 +752,7 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { GlobalAlloc::Static(tables.static_def(*def)) } mir::interpret::GlobalAlloc::Memory(alloc) => GlobalAlloc::Memory(alloc.stable(tables)), + mir::interpret::GlobalAlloc::Type(_) => todo!(), } } } From dd2afeb7d51467f33c357b1cda3a77d3381a642b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Jun 2025 08:19:35 +0000 Subject: [PATCH 06/12] Change type id to contain a pointer to the unique type id information --- library/core/src/any.rs | 52 +++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 01dce114592a1..599e1194430eb 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -708,9 +708,15 @@ impl dyn Any + Send + Sync { #[derive(Clone, Copy, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - // We avoid using `u128` because that imposes higher alignment requirements on many platforms. - // See issue #115620 for more information. - t: (u64, u64), + /// Quick accept: if pointers are the same, the ids are the same + data: &'static TypeIdData, + /// Quick reject: if hashes are different, the ids are different + partial_hash: usize, +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct TypeIdData { + full_hash: [u8; 16], #[cfg(feature = "debug_typeid")] name: &'static str, } @@ -719,7 +725,16 @@ pub struct TypeId { impl PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - self.t == other.t + self.data as *const TypeIdData == other.data as *const TypeIdData + || (self.partial_hash == other.partial_hash + && self.data.full_hash == other.data.full_hash) + } + + #[inline] + fn ne(&self, other: &Self) -> bool { + self.partial_hash != other.partial_hash + || (self.data as *const TypeIdData != other.data as *const TypeIdData + && self.data.full_hash != other.data.full_hash) } } @@ -742,19 +757,21 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - let t: u128 = const { intrinsics::type_id::() }; - let t1 = (t >> 64) as u64; - let t2 = t as u64; - - TypeId { - t: (t1, t2), - #[cfg(feature = "debug_typeid")] - name: type_name::(), - } + let data = &const { + let t: u128 = intrinsics::type_id::(); + TypeIdData { + full_hash: t.to_ne_bytes(), + + #[cfg(feature = "debug_typeid")] + name: type_name::(), + } + }; + + TypeId { data, partial_hash: intrinsics::type_id::() as usize } } fn as_u128(self) -> u128 { - u128::from(self.t.0) << 64 | u128::from(self.t.1) + u128::from_ne_bytes(self.data.full_hash) } } @@ -774,7 +791,12 @@ impl hash::Hash for TypeId { // - It is correct to do so -- only hashing a subset of `self` is still // compatible with an `Eq` implementation that considers the entire // value, as ours does. - self.t.1.hash(state); + if cfg!(target_pointer_width = "64") { + self.partial_hash.hash(state); + } else { + let [_, _, _, _, _, _, _, _, data @ ..] = self.data.full_hash; + u64::from_ne_bytes(data).hash(state); + } } } From 9c3f8a95bb2aacac71e3572cba37b03047145487 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Jun 2025 08:22:04 +0000 Subject: [PATCH 07/12] Some simplifications --- library/core/Cargo.toml | 2 -- library/core/src/any.rs | 37 +++++++++++++++++----------------- library/coretests/tests/any.rs | 8 -------- library/std/Cargo.toml | 2 -- library/sysroot/Cargo.toml | 1 - 5 files changed, 18 insertions(+), 32 deletions(-) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 5d65b55bcdabe..31900bcc0878c 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -23,8 +23,6 @@ optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] -# Make `TypeId` store a reference to the name of the type, so that it can print that name. -debug_typeid = [] [lints.rust.unexpected_cfgs] level = "warn" diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 599e1194430eb..5bde71d68a8d3 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -711,14 +711,23 @@ pub struct TypeId { /// Quick accept: if pointers are the same, the ids are the same data: &'static TypeIdData, /// Quick reject: if hashes are different, the ids are different - partial_hash: usize, + /// We use a raw pointer instead of a `usize`, because const eval + /// will be storing this as a fake pointer that will turn into the + /// appropriate bits at codegen time. This prevents users from inspecting + /// these bits at compile time. + partial_hash: *const (), } +// SAFETY: the raw pointer is always an integer +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for TypeId {} +// SAFETY: the raw pointer is always an integer +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for TypeId {} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct TypeIdData { full_hash: [u8; 16], - #[cfg(feature = "debug_typeid")] - name: &'static str, } #[stable(feature = "rust1", since = "1.0.0")] @@ -759,15 +768,13 @@ impl TypeId { pub const fn of() -> TypeId { let data = &const { let t: u128 = intrinsics::type_id::(); - TypeIdData { - full_hash: t.to_ne_bytes(), - - #[cfg(feature = "debug_typeid")] - name: type_name::(), - } + TypeIdData { full_hash: t.to_ne_bytes() } }; - TypeId { data, partial_hash: intrinsics::type_id::() as usize } + TypeId { + data, + partial_hash: crate::ptr::without_provenance(intrinsics::type_id::() as usize), + } } fn as_u128(self) -> u128 { @@ -803,15 +810,7 @@ impl hash::Hash for TypeId { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for TypeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - #[cfg(feature = "debug_typeid")] - { - write!(f, "TypeId({:#034x} = {})", self.as_u128(), self.name)?; - } - #[cfg(not(feature = "debug_typeid"))] - { - write!(f, "TypeId({:#034x})", self.as_u128())?; - } - Ok(()) + write!(f, "TypeId({:#034x})", self.as_u128()) } } diff --git a/library/coretests/tests/any.rs b/library/coretests/tests/any.rs index 117ef0042380d..25002617d0bbd 100644 --- a/library/coretests/tests/any.rs +++ b/library/coretests/tests/any.rs @@ -118,14 +118,6 @@ fn any_unsized() { is_any::<[i32]>(); } -#[cfg(feature = "debug_typeid")] -#[test] -fn debug_typeid_includes_name() { - let type_id = TypeId::of::<[usize; 2]>(); - let debug_str = format!("{type_id:?}"); - assert!(debug_str.ends_with("= [usize; 2])"), "{debug_str:?} did not match"); -} - #[test] fn distinct_type_names() { // https://github.com/rust-lang/rust/issues/84666 diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ae7107938f363..969a0c6b77166 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -113,8 +113,6 @@ optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = ["core/debug_refcell"] -# Make `TypeId` store a reference to the name of the type, so that it can print that name. -debug_typeid = ["core/debug_typeid"] # Enable std_detect default features for stdarch/crates/std_detect: diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index c149d513c32b4..d1cd1473cf50b 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -22,7 +22,6 @@ compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] debug_refcell = ["std/debug_refcell"] -debug_typeid = ["std/debug_typeid"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] optimize_for_size = ["std/optimize_for_size"] From 061fa5a53ca5613d871d7ee913f48d43e46b3d66 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Jun 2025 08:22:04 +0000 Subject: [PATCH 08/12] Make TypeId robust against compile-time introspection --- compiler/rustc_codegen_llvm/src/common.rs | 10 +++ .../src/interpret/intrinsics.rs | 30 +++++++- .../rustc_const_eval/src/interpret/memory.rs | 21 ++++-- .../src/interpret/validity.rs | 68 ++++++++++--------- compiler/rustc_hir/src/lang_items.rs | 2 + .../rustc_hir_analysis/src/check/intrinsic.rs | 4 +- .../rustc_middle/src/mir/interpret/mod.rs | 35 ++++++++-- compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 1 + compiler/rustc_passes/src/reachable.rs | 2 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + library/core/src/any.rs | 11 +-- library/core/src/intrinsics/mod.rs | 2 +- 14 files changed, 135 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index aa47bc7b61fc3..dccb73277d7bb 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -342,9 +342,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { Scalar::from_u128(type_id), ) .unwrap(); + alloc.mutability = Mutability::Not; let init = const_alloc_to_llvm(self, &alloc, /*static*/ false); self.static_addr_of_impl(init, alloc.align, None) } + GlobalAlloc::PartialHash(ty) => { + assert!(matches!(layout.primitive(), Pointer(_))); + let bytes = self.tcx.type_id_hash(ty).truncate().as_u64().to_be_bytes(); + let bits = self.tcx.data_layout.pointer_size.bits(); + let mask = u64::MAX >> (64 - bits); + // It doesn't matter which bits we pick as long as the scheme is the same with the same compiler. + let llval = self.const_usize(u64::from_be_bytes(bytes) & mask); + return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; + } }; let base_addr_space = global_alloc.address_space(self); let llval = unsafe { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 1b56d70865866..419b2e9124d5a 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,6 +6,8 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_ast::Mutability; +use rustc_middle::mir::interpret::{AllocId, AllocInit, CtfeProvenance, alloc_range}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{Ty, TyCtxt}; @@ -29,6 +31,31 @@ 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 ptr_size = tcx.data_layout.pointer_size; + let align = tcx.data_layout.pointer_align; + + let mut alloc = Allocation::new(ptr_size * 2, *align, AllocInit::Uninit, ()); + // Write a pointer pointing to the hash. At ctfe time this is an opaque pointer that you cannot deref + let ptr = tcx.reserve_and_set_type_id_alloc(ty); + let ptr = Pointer::new(CtfeProvenance::from(ptr), Size::ZERO); + alloc + .write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), Scalar::from_pointer(ptr, &tcx)) + .unwrap(); + + // Write a pointer that is not actually a pointer but will just get replaced by the first `pointer_size` bytes of the hash + // in codegen. It is a pointer in CTFE so no one can access the bits. + let ptr = tcx.reserve_and_set_type_id_partial_hash(ty); + let ptr = Pointer::new(CtfeProvenance::from(ptr), Size::ZERO); + alloc + .write_scalar(&tcx, alloc_range(ptr_size, ptr_size), Scalar::from_pointer(ptr, &tcx)) + .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,7 +90,8 @@ 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)?; } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index fc610bb7fedec..188a7ad7fc5c0 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -353,6 +353,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "typeid", ) } + Some(GlobalAlloc::PartialHash(..)) => { + err_ub_custom!( + fluent::const_eval_invalid_dealloc, + alloc_id = alloc_id, + kind = "partial_hash", + ) + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, @@ -622,7 +629,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), - Some(GlobalAlloc::Type(..)) => throw_ub!(DerefTypeIdPointer(id)), + Some(GlobalAlloc::Type(..)) | Some(GlobalAlloc::PartialHash(..)) => { + throw_ub!(DerefTypeIdPointer(id)) + } None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -899,9 +908,10 @@ 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::Type(_) | GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => { - AllocKind::LiveData - } + GlobalAlloc::Type(_) + | GlobalAlloc::PartialHash(_) + | GlobalAlloc::Static { .. } + | GlobalAlloc::Memory { .. } => AllocKind::LiveData, GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), GlobalAlloc::VTable { .. } => AllocKind::VTable, }; @@ -1214,6 +1224,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::Type(ty)) => { write!(fmt, " (typeid for {ty})")?; } + Some(GlobalAlloc::PartialHash(ty)) => { + write!(fmt, " (partial hash of {ty})")?; + } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 7e26b7f3d9629..ed1d9d07f97dd 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -573,40 +573,46 @@ 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; + } } } } + // These are controlled by rustc and not available for CTFE + GlobalAlloc::Type(_) | GlobalAlloc::PartialHash(_) => { + skip_recursive_check = true + } + _ => (), } // If this allocation has size zero, there is no actual mutability here. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3a08e5ae3365f..964d7847cb2e9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -274,6 +274,8 @@ language_item_table! { PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; + // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. // diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 060fc51b7bda7..69576960459b1 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -220,7 +220,9 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => (1, 0, vec![], tcx.types.u128), + sym::type_id => { + (1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()) + } sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 563970830ecde..d0ec50547eebc 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -104,6 +104,7 @@ enum AllocDiscriminant { VTable, Static, Type, + PartialHash, } pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( @@ -133,6 +134,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( AllocDiscriminant::Type.encode(encoder); ty.encode(encoder); } + GlobalAlloc::PartialHash(ty) => { + trace!("encoding {alloc_id:?} with {ty:#?}"); + AllocDiscriminant::PartialHash.encode(encoder); + ty.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -240,6 +246,12 @@ impl<'s> AllocDecodingSession<'s> { trace!("decoded typid: {ty:?}"); decoder.interner().reserve_and_set_type_id_alloc(ty) } + AllocDiscriminant::PartialHash => { + trace!("creating typeid alloc ID"); + let ty = Decodable::decode(decoder); + trace!("decoded typid: {ty:?}"); + decoder.interner().reserve_and_set_type_id_partial_hash(ty) + } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); let did = >::decode(decoder); @@ -270,9 +282,10 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), - /// A TypeId pointer. For now cannot be turned into a runtime value. - /// TODO: turn into actual TypeId? + /// A pointer to be stored within a TypeId pointing to the full hash. Type(Ty<'tcx>), + /// A partial type hash (the first `size_of()` bytes of the hash). + PartialHash(Ty<'tcx>), } impl<'tcx> GlobalAlloc<'tcx> { @@ -312,6 +325,7 @@ impl<'tcx> GlobalAlloc<'tcx> { match self { GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, GlobalAlloc::Type(_) + | GlobalAlloc::PartialHash(_) | GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => AddressSpace::DATA, @@ -350,7 +364,10 @@ impl<'tcx> GlobalAlloc<'tcx> { } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Type(_) | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { + GlobalAlloc::PartialHash(_) + | GlobalAlloc::Type(_) + | GlobalAlloc::Function { .. } + | GlobalAlloc::VTable(..) => { // These are immutable. Mutability::Not } @@ -398,8 +415,8 @@ impl<'tcx> GlobalAlloc<'tcx> { // No data to be accessed here. But vtables are pointer-aligned. (Size::ZERO, tcx.data_layout.pointer_align.abi) } - // TODO make this represent normal type ids somehow - GlobalAlloc::Type(_) => (Size::from_bytes(16), Align::from_bytes(8).unwrap()), + GlobalAlloc::Type(_) => (Size::from_bytes(16), Align::ONE), + GlobalAlloc::PartialHash(_) => (Size::ZERO, Align::ONE), } } } @@ -505,11 +522,17 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } - /// Generates an [AllocId] for a [core::mem::type_info::TypeId]. Will get deduplicated. + /// Generates an [AllocId] for a [core::mem::type_info::TypeIdData]. Will get deduplicated. pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { self.reserve_and_set_dedup(GlobalAlloc::Type(ty), 0) } + /// Generates an [AllocId] that will get replaced by a partial hash of a type. + /// See [core::mem::type_info::TypeId] for more information. + pub fn reserve_and_set_type_id_partial_hash(self, ty: Ty<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::PartialHash(ty), 0) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 6250e501f9973..f5eb7d771dc31 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1622,6 +1622,7 @@ pub fn write_allocations<'tcx>( write!(w, " (vtable: impl {dyn_ty} for {ty})")? } Some(GlobalAlloc::Type(ty)) => write!(w, " (typeid for {ty})")?, + Some(GlobalAlloc::PartialHash(ty)) => write!(w, " (partial hash of {ty})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 87d10a86ac427..c582f5e83c65f 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1781,6 +1781,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Some(GlobalAlloc::Function { .. }) => p!(""), Some(GlobalAlloc::VTable(..)) => p!(""), Some(GlobalAlloc::Type(_)) => p!(""), + Some(GlobalAlloc::PartialHash(_)) => p!(""), None => p!(""), } return Ok(()); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 75e94deabca58..9212da9a55ee3 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1228,6 +1228,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt collect_alloc(tcx, alloc_id, output) } GlobalAlloc::Type(_) => {} + GlobalAlloc::PartialHash(_) => {} } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index ed3260503fdcd..fea3dfa3059fe 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -325,7 +325,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(args); } } - GlobalAlloc::Type(ty) => self.visit(ty), + GlobalAlloc::PartialHash(ty) | GlobalAlloc::Type(ty) => self.visit(ty), GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 9f4c8f4de58c9..834ba019066f0 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -753,6 +753,7 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { } mir::interpret::GlobalAlloc::Memory(alloc) => GlobalAlloc::Memory(alloc.stable(tables)), mir::interpret::GlobalAlloc::Type(_) => todo!(), + mir::interpret::GlobalAlloc::PartialHash(_) => todo!(), } } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 5bde71d68a8d3..f3f42ac08654c 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -707,6 +707,7 @@ impl dyn Any + Send + Sync { /// ``` #[derive(Clone, Copy, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] +#[lang = "type_id"] pub struct TypeId { /// Quick accept: if pointers are the same, the ids are the same data: &'static TypeIdData, @@ -766,15 +767,7 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - let data = &const { - let t: u128 = intrinsics::type_id::(); - TypeIdData { full_hash: t.to_ne_bytes() } - }; - - TypeId { - data, - partial_hash: crate::ptr::without_provenance(intrinsics::type_id::() as usize), - } + const { intrinsics::type_id::() } } fn as_u128(self) -> u128 { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4250de9fb2b33..f3be88668d419 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2718,7 +2718,7 @@ pub const fn type_name() -> &'static str; #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] -pub const fn type_id() -> u128; +pub const fn type_id() -> crate::any::TypeId; /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// From 97b0afb5c6e66f5c3e56d09895d67f76eb9f153b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 20 Jun 2025 14:18:56 +0000 Subject: [PATCH 09/12] Make TypeId const-comparable --- .../src/const_eval/machine.rs | 17 +++++++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 5 +++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/any.rs | 34 ++++++++++++++----- library/core/src/intrinsics/mod.rs | 12 +++++++ tests/ui/const-generics/issues/issue-90318.rs | 3 +- .../const-generics/issues/issue-90318.stderr | 25 ++------------ tests/ui/consts/const_cmp_type_id.rs | 3 +- tests/ui/consts/const_cmp_type_id.stderr | 20 +++++------ tests/ui/consts/issue-73976-monomorphic.rs | 2 +- .../ui/consts/issue-73976-monomorphic.stderr | 9 ----- 11 files changed, 74 insertions(+), 57 deletions(-) delete mode 100644 tests/ui/consts/issue-73976-monomorphic.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index a68dcf2998866..aef005087d6f0 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt; use std::hash::Hash; -use rustc_abi::{Align, Size}; +use rustc_abi::{Align, FieldIdx, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -403,6 +403,21 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let cmp = ecx.guaranteed_cmp(a, b)?; ecx.write_scalar(Scalar::from_u8(cmp), dest)?; } + sym::type_id_eq => { + let a = ecx.project_field(&args[0], FieldIdx::ZERO)?; + let a = ecx.deref_pointer(&a)?; + let (a, offset) = a.ptr().into_parts(); + assert_eq!(offset, Size::ZERO); + let a = a.unwrap().alloc_id(); + let GlobalAlloc::Type(a) = ecx.tcx.global_alloc(a) else { bug!() }; + let b = ecx.project_field(&args[1], FieldIdx::ZERO)?; + let b = ecx.deref_pointer(&b)?; + let (b, offset) = b.ptr().into_parts(); + assert_eq!(offset, Size::ZERO); + let b = b.unwrap().alloc_id(); + let GlobalAlloc::Type(b) = ecx.tcx.global_alloc(b) else { bug!() }; + ecx.write_scalar(Scalar::from_bool(a == b), dest)?; + } sym::const_allocate => { let size = ecx.read_scalar(&args[0])?.to_target_usize(ecx)?; let align = ecx.read_scalar(&args[1])?.to_target_usize(ecx)?; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 69576960459b1..2817357f5e1e8 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -93,6 +93,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::three_way_compare | sym::discriminant_value | sym::type_id + | sym::type_id_eq | sym::select_unpredictable | sym::cold_path | sym::ptr_guaranteed_cmp @@ -223,6 +224,10 @@ pub(crate) fn check_intrinsic_type( sym::type_id => { (1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()) } + sym::type_id_eq => { + let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity(); + (0, 0, vec![type_id, type_id], tcx.types.bool) + } sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 368944ffd5cce..480f505dabe5b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2195,6 +2195,7 @@ symbols! { type_changing_struct_update, type_const, type_id, + type_id_eq, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, diff --git a/library/core/src/any.rs b/library/core/src/any.rs index f3f42ac08654c..5f6fbda58f451 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -727,24 +727,42 @@ unsafe impl Send for TypeId {} unsafe impl Sync for TypeId {} #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct TypeIdData { +pub(crate) struct TypeIdData { full_hash: [u8; 16], } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for TypeId { +#[rustc_const_unstable(feature = "const_type_id", issue = "77125")] +impl const PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - self.data as *const TypeIdData == other.data as *const TypeIdData - || (self.partial_hash == other.partial_hash - && self.data.full_hash == other.data.full_hash) + const fn ct(a: &TypeId, b: &TypeId) -> bool { + crate::intrinsics::type_id_eq(*a, *b) + } + + #[inline] + fn rt(a: &TypeId, b: &TypeId) -> bool { + a.data as *const TypeIdData == b.data as *const TypeIdData + || (a.partial_hash == b.partial_hash && a.data.full_hash == b.data.full_hash) + } + + core::intrinsics::const_eval_select((self, other), ct, rt) } #[inline] fn ne(&self, other: &Self) -> bool { - self.partial_hash != other.partial_hash - || (self.data as *const TypeIdData != other.data as *const TypeIdData - && self.data.full_hash != other.data.full_hash) + const fn ct(a: &TypeId, b: &TypeId) -> bool { + !crate::intrinsics::type_id_eq(*a, *b) + } + + #[inline] + fn rt(a: &TypeId, b: &TypeId) -> bool { + a.partial_hash != b.partial_hash + || (a.data as *const TypeIdData != b.data as *const TypeIdData + && a.data.full_hash != b.data.full_hash) + } + + core::intrinsics::const_eval_select((self, other), ct, rt) } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index f3be88668d419..499e816c10d1f 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2720,6 +2720,18 @@ pub const fn type_name() -> &'static str; #[rustc_intrinsic] pub const fn type_id() -> crate::any::TypeId; +/// Tests (at compile-time) if two [`crate::any::TypeId`] instances identify the +/// same type. This is necessary because at const-eval time the actual discriminating +/// data is opaque and cannot be inspected directly. +/// +/// The stabilized version of this intrinsic is the [PartialEq] impl for [`core::any::TypeId`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +pub const fn type_id_eq(_a: crate::any::TypeId, _b: crate::any::TypeId) -> bool { + panic!("type_id_eq should only be used from const eval") +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/tests/ui/const-generics/issues/issue-90318.rs b/tests/ui/const-generics/issues/issue-90318.rs index 317ddad49cd4e..dfba90a557512 100644 --- a/tests/ui/const-generics/issues/issue-90318.rs +++ b/tests/ui/const-generics/issues/issue-90318.rs @@ -1,5 +1,6 @@ #![feature(const_type_id)] #![feature(generic_const_exprs)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] #![allow(incomplete_features)] @@ -13,7 +14,6 @@ fn consume(_val: T) where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, //~^ ERROR overly complex generic constant - //~| ERROR: cannot call { } @@ -21,7 +21,6 @@ fn test() where If<{ TypeId::of::() != TypeId::of::<()>() }>: True, //~^ ERROR overly complex generic constant - //~| ERROR: cannot call { } diff --git a/tests/ui/const-generics/issues/issue-90318.stderr b/tests/ui/const-generics/issues/issue-90318.stderr index 9c7cb5ceb58c0..7031230db91c3 100644 --- a/tests/ui/const-generics/issues/issue-90318.stderr +++ b/tests/ui/const-generics/issues/issue-90318.stderr @@ -1,5 +1,5 @@ error: overly complex generic constant - --> $DIR/issue-90318.rs:14:8 + --> $DIR/issue-90318.rs:15:8 | LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,26 +20,5 @@ LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, = help: consider moving this anonymous constant into a `const` function = note: this operation may be supported in the future -error[E0015]: cannot call non-const operator in constants - --> $DIR/issue-90318.rs:14:10 - | -LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error[E0015]: cannot call non-const operator in constants - --> $DIR/issue-90318.rs:22:10 - | -LL | If<{ TypeId::of::() != TypeId::of::<()>() }>: True, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const_cmp_type_id.rs b/tests/ui/consts/const_cmp_type_id.rs index dca0615083a78..def615bd92bb5 100644 --- a/tests/ui/consts/const_cmp_type_id.rs +++ b/tests/ui/consts/const_cmp_type_id.rs @@ -6,10 +6,9 @@ use std::any::TypeId; fn main() { const { assert!(TypeId::of::() == TypeId::of::()); - //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied assert!(TypeId::of::<()>() != TypeId::of::()); - //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied let _a = TypeId::of::() < TypeId::of::(); + //~^ ERROR: cannot call non-const operator in constants // can't assert `_a` because it is not deterministic // FIXME(const_trait_impl) make it pass } diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr index a8242a200eff1..540eec5098b40 100644 --- a/tests/ui/consts/const_cmp_type_id.stderr +++ b/tests/ui/consts/const_cmp_type_id.stderr @@ -1,15 +1,13 @@ -error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied - --> $DIR/const_cmp_type_id.rs:8:17 +error[E0015]: cannot call non-const operator in constants + --> $DIR/const_cmp_type_id.rs:10:18 | -LL | assert!(TypeId::of::() == TypeId::of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied - --> $DIR/const_cmp_type_id.rs:10:17 +LL | let _a = TypeId::of::() < TypeId::of::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | assert!(TypeId::of::<()>() != TypeId::of::()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: impl defined here, but it is not `const` + --> $SRC_DIR/core/src/any.rs:LL:COL + = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/issue-73976-monomorphic.rs b/tests/ui/consts/issue-73976-monomorphic.rs index 561c197605117..3bfdb397afb23 100644 --- a/tests/ui/consts/issue-73976-monomorphic.rs +++ b/tests/ui/consts/issue-73976-monomorphic.rs @@ -1,4 +1,4 @@ -//@ known-bug: #110395 +//@ check-pass // // This test is complement to the test in issue-73976-polymorphic.rs. // In that test we ensure that polymorphic use of type_id and type_name in patterns diff --git a/tests/ui/consts/issue-73976-monomorphic.stderr b/tests/ui/consts/issue-73976-monomorphic.stderr deleted file mode 100644 index e5b32e0c4adff..0000000000000 --- a/tests/ui/consts/issue-73976-monomorphic.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `TypeId: ~const PartialEq` is not satisfied - --> $DIR/issue-73976-monomorphic.rs:21:5 - | -LL | GetTypeId::::VALUE == GetTypeId::::VALUE - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. From eaf64e3274129b0f51b942e4584438b8069eb3bf Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 11 Apr 2025 09:01:34 +0000 Subject: [PATCH 10/12] Split up TypeId into pointer sized fields for CTFE to use as opaque ids --- .../rustc_codegen_cranelift/src/constant.rs | 44 ++++++++++++++-- compiler/rustc_codegen_cranelift/src/lib.rs | 1 + compiler/rustc_codegen_gcc/src/common.rs | 30 +++++++++-- compiler/rustc_codegen_gcc/src/lib.rs | 1 + compiler/rustc_codegen_llvm/src/common.rs | 27 ++++------ compiler/rustc_codegen_ssa/src/mir/operand.rs | 2 +- .../src/const_eval/machine.rs | 38 ++++++++++---- .../src/interpret/intrinsics.rs | 31 +++++------ .../rustc_const_eval/src/interpret/memory.rs | 23 ++------- .../src/interpret/validity.rs | 4 +- .../rustc_middle/src/mir/interpret/mod.rs | 51 ++++++------------- compiler/rustc_middle/src/mir/pretty.rs | 5 +- compiler/rustc_middle/src/ty/print/pretty.rs | 3 +- compiler/rustc_monomorphize/src/collector.rs | 3 +- compiler/rustc_passes/src/reachable.rs | 2 +- .../rustc_smir/src/rustc_smir/convert/mir.rs | 3 +- library/core/src/any.rs | 33 +++--------- tests/ui/consts/const_transmute_type_id.rs | 11 ++++ .../ui/consts/const_transmute_type_id.stderr | 12 +++++ 19 files changed, 177 insertions(+), 147 deletions(-) create mode 100644 tests/ui/consts/const_transmute_type_id.rs create mode 100644 tests/ui/consts/const_transmute_type_id.stderr diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 07c58eb05d166..5b2459a7d6e8b 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -3,6 +3,8 @@ use std::cmp::Ordering; use cranelift_module::*; +use rustc_abi::Align; +use rustc_const_eval::interpret::{AllocInit, Allocation, alloc_range}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint}; @@ -175,8 +177,13 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - // TODO: generate pointer to allocation containing the actual type id hash u128 value - GlobalAlloc::Type(_) => todo!(), + GlobalAlloc::Type { ty: type_id_ty, segment } => { + return CValue::const_val( + fx, + layout, + type_id_segment(fx.tcx, type_id_ty, segment), + ); + } GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static( @@ -214,6 +221,23 @@ pub(crate) fn codegen_const_value<'tcx>( } } +fn type_id_segment<'tcx>(tcx: TyCtxt<'tcx>, type_id_ty: Ty<'tcx>, segment: u8) -> ScalarInt { + let type_id = tcx.type_id_hash(type_id_ty).as_u128(); + let mut alloc: Allocation = + Allocation::new(Size::from_bytes(16), Align::from_bytes(8).unwrap(), AllocInit::Uninit, ()); + alloc + .write_scalar( + &tcx, + alloc_range(Size::ZERO, Size::from_bytes(16)), + Scalar::from_u128(type_id), + ) + .unwrap(); + let pointer_size = tcx.data_layout.pointer_size; + let offset = pointer_size * u64::from(segment); + let value = alloc.read_scalar(&tcx, alloc_range(offset, pointer_size), false).unwrap(); + value.to_scalar_int().unwrap() +} + fn pointer_for_allocation<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, alloc_id: AllocId, @@ -362,7 +386,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(alloc) => alloc, GlobalAlloc::Function { .. } | GlobalAlloc::Static(_) - | GlobalAlloc::Type(_) + | GlobalAlloc::Type { .. } | GlobalAlloc::VTable(..) => { unreachable!() } @@ -474,8 +498,18 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant .principal() .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), ), - // TODO - GlobalAlloc::Type(_ty) => todo!(), + GlobalAlloc::Type { ty, segment } => { + let val = type_id_segment(tcx, ty, segment); + let Init::Bytes { contents } = &mut data.init else { unreachable!() }; + let start = offset.bytes_usize(); + let ptr_size = tcx.data_layout.pointer_size; + let src = val.to_bits(ptr_size); + let src = u128::to_le_bytes(src); + let len = ptr_size.bytes_usize(); + assert!(src[len..].iter().all(|b| *b == 0)); + contents[start..(start + len)].copy_from_slice(&src[..len]); + continue; + } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8e34436fb5e0a..41d0f8ddd7237 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -20,6 +20,7 @@ extern crate rustc_middle; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_codegen_ssa; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_fs_util; diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 44f484b6e633e..b2b8127b88933 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,10 +1,10 @@ use gccjit::{LValue, RValue, ToRValue, Type}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, Align, HasDataLayout, Size}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; +use rustc_const_eval::interpret::{AllocInit, Allocation, alloc_range}; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::layout::LayoutOf; @@ -303,8 +303,30 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { let init = self.const_data_from_alloc(alloc); self.static_addr_of(init, alloc.inner().align, None) } - // TODO: generate pointer to allocation containing the actual type id hash u128 value - GlobalAlloc::Type(_ty) => todo!(), + GlobalAlloc::Type { ty: type_id_ty, segment } => { + let type_id = self.tcx.type_id_hash(type_id_ty).as_u128(); + let mut alloc: Allocation = Allocation::new( + Size::from_bytes(16), + Align::from_bytes(8).unwrap(), + AllocInit::Uninit, + (), + ); + alloc + .write_scalar( + &self.tcx, + alloc_range(Size::ZERO, Size::from_bytes(16)), + Scalar::from_u128(type_id), + ) + .unwrap(); + let pointer_size = self.tcx.data_layout.pointer_size; + let offset = pointer_size * u64::from(segment); + let value = alloc + .read_scalar(&self.tcx, alloc_range(offset, pointer_size), false) + .unwrap(); + let data = value.to_bits(pointer_size).unwrap() as u64; + let val = self.const_usize(data); + return self.context.new_cast(None, val, ty); + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a912678ef2a10..b514010db1a77 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -41,6 +41,7 @@ extern crate rustc_apfloat; extern crate rustc_ast; extern crate rustc_attr_data_structures; extern crate rustc_codegen_ssa; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_fluent_macro; diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index dccb73277d7bb..105ab667fa4a9 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,7 +4,7 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, Align, HasDataLayout, Size}; +use rustc_abi::{self as abi, Align, HasDataLayout as _, Size}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -319,17 +319,16 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { ))) .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); - let value = self.static_addr_of_impl(init, alloc.inner().align, None); - value + self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } - GlobalAlloc::Type(ty) => { + GlobalAlloc::Type { ty, segment } => { let type_id = self.tcx.type_id_hash(ty).as_u128(); - let mut alloc = Allocation::new( + let mut alloc: Allocation = Allocation::new( Size::from_bytes(16), Align::from_bytes(8).unwrap(), AllocInit::Uninit, @@ -342,17 +341,13 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { Scalar::from_u128(type_id), ) .unwrap(); - alloc.mutability = Mutability::Not; - let init = const_alloc_to_llvm(self, &alloc, /*static*/ false); - self.static_addr_of_impl(init, alloc.align, None) - } - GlobalAlloc::PartialHash(ty) => { - assert!(matches!(layout.primitive(), Pointer(_))); - let bytes = self.tcx.type_id_hash(ty).truncate().as_u64().to_be_bytes(); - let bits = self.tcx.data_layout.pointer_size.bits(); - let mask = u64::MAX >> (64 - bits); - // It doesn't matter which bits we pick as long as the scheme is the same with the same compiler. - let llval = self.const_usize(u64::from_be_bytes(bytes) & mask); + let pointer_size = self.tcx.data_layout.pointer_size; + let offset = pointer_size * u64::from(segment); + let value = alloc + .read_scalar(&self.tcx, alloc_range(offset, pointer_size), false) + .unwrap(); + let data = value.to_bits(pointer_size).unwrap() as u64; + let llval = self.const_usize(data); return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; } }; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 99957c6770845..cce2a788280dc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -185,7 +185,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { offset: Size, ) -> Self { let alloc_align = alloc.inner().align; - assert!(alloc_align >= layout.align.abi); + assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi); let read_scalar = |start, size, s: abi::Scalar, ty| { match alloc.0.read_scalar( diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index aef005087d6f0..b79360af0868c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -405,18 +405,34 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } sym::type_id_eq => { let a = ecx.project_field(&args[0], FieldIdx::ZERO)?; - let a = ecx.deref_pointer(&a)?; - let (a, offset) = a.ptr().into_parts(); - assert_eq!(offset, Size::ZERO); - let a = a.unwrap().alloc_id(); - let GlobalAlloc::Type(a) = ecx.tcx.global_alloc(a) else { bug!() }; let b = ecx.project_field(&args[1], FieldIdx::ZERO)?; - let b = ecx.deref_pointer(&b)?; - let (b, offset) = b.ptr().into_parts(); - assert_eq!(offset, Size::ZERO); - let b = b.unwrap().alloc_id(); - let GlobalAlloc::Type(b) = ecx.tcx.global_alloc(b) else { bug!() }; - ecx.write_scalar(Scalar::from_bool(a == b), dest)?; + let mut eq = true; + for index in 0..(16 / ecx.tcx.data_layout.pointer_size.bytes()) { + let a = ecx.project_index(&a, index)?; + let a = ecx.deref_pointer(&a)?; + let (a, offset) = a.ptr().into_parts(); + assert_eq!(offset, Size::ZERO); + let a = a.unwrap().alloc_id(); + let GlobalAlloc::Type { ty: a, segment: a_segment } = ecx.tcx.global_alloc(a) + else { + bug!() + }; + let b = ecx.project_index(&b, index)?; + let b = ecx.deref_pointer(&b)?; + let (b, offset) = b.ptr().into_parts(); + assert_eq!(offset, Size::ZERO); + let b = b.unwrap().alloc_id(); + let GlobalAlloc::Type { ty: b, segment: b_segment } = ecx.tcx.global_alloc(b) + else { + bug!() + }; + + eq &= a == b && a_segment == b_segment; + if !eq { + break; + } + } + ecx.write_scalar(Scalar::from_bool(eq), dest)?; } sym::const_allocate => { let size = ecx.read_scalar(&args[0])?.to_target_usize(ecx)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 419b2e9124d5a..3005283d7e28d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::assert_matches::assert_matches; use rustc_abi::Size; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_ast::Mutability; -use rustc_middle::mir::interpret::{AllocId, AllocInit, CtfeProvenance, alloc_range}; +use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{Ty, TyCtxt}; @@ -32,24 +32,17 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll } pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { - let ptr_size = tcx.data_layout.pointer_size; + let size = Size::from_bytes(16); let align = tcx.data_layout.pointer_align; - - let mut alloc = Allocation::new(ptr_size * 2, *align, AllocInit::Uninit, ()); - // Write a pointer pointing to the hash. At ctfe time this is an opaque pointer that you cannot deref - let ptr = tcx.reserve_and_set_type_id_alloc(ty); - let ptr = Pointer::new(CtfeProvenance::from(ptr), Size::ZERO); - alloc - .write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), Scalar::from_pointer(ptr, &tcx)) - .unwrap(); - - // Write a pointer that is not actually a pointer but will just get replaced by the first `pointer_size` bytes of the hash - // in codegen. It is a pointer in CTFE so no one can access the bits. - let ptr = tcx.reserve_and_set_type_id_partial_hash(ty); - let ptr = Pointer::new(CtfeProvenance::from(ptr), Size::ZERO); - alloc - .write_scalar(&tcx, alloc_range(ptr_size, ptr_size), Scalar::from_pointer(ptr, &tcx)) - .unwrap(); + let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ()); + let ptr_size = tcx.data_layout.pointer_size; + for step in 0..size.bytes() / ptr_size.bytes() { + let offset = ptr_size * step; + let alloc_id = tcx.reserve_and_set_type_id_alloc(ty, step.try_into().unwrap()); + let ptr = Pointer::new(alloc_id.into(), Size::ZERO); + let val = Scalar::from_pointer(ptr, &tcx); + alloc.write_scalar(&tcx, alloc_range(offset, ptr_size), val).unwrap(); + } alloc.mutability = Mutability::Not; @@ -90,7 +83,7 @@ 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 alloc_id = alloc_type_id(tcx, tp_ty); + 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)?; diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 188a7ad7fc5c0..6b35071f653ad 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -346,20 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "vtable", ) } - Some(GlobalAlloc::Type(..)) => { + Some(GlobalAlloc::Type { .. }) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, alloc_id = alloc_id, kind = "typeid", ) } - Some(GlobalAlloc::PartialHash(..)) => { - err_ub_custom!( - fluent::const_eval_invalid_dealloc, - alloc_id = alloc_id, - kind = "partial_hash", - ) - } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, @@ -629,9 +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::Type(..)) | Some(GlobalAlloc::PartialHash(..)) => { - throw_ub!(DerefTypeIdPointer(id)) - } + Some(GlobalAlloc::Type { .. }) => throw_ub!(DerefTypeIdPointer(id)), None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -908,8 +899,7 @@ 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::Type(_) - | GlobalAlloc::PartialHash(_) + GlobalAlloc::Type { .. } | GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData, GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), @@ -1221,11 +1211,8 @@ 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::Type(ty)) => { - write!(fmt, " (typeid for {ty})")?; - } - Some(GlobalAlloc::PartialHash(ty)) => { - write!(fmt, " (partial hash of {ty})")?; + Some(GlobalAlloc::Type { ty, segment }) => { + write!(fmt, " (typeid segment {segment } 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/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index ed1d9d07f97dd..c63752b995ffe 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -609,9 +609,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } } // These are controlled by rustc and not available for CTFE - GlobalAlloc::Type(_) | GlobalAlloc::PartialHash(_) => { - skip_recursive_check = true - } + GlobalAlloc::Type { .. } => skip_recursive_check = true, _ => (), } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index d0ec50547eebc..59f8509d1b595 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -104,7 +104,6 @@ enum AllocDiscriminant { VTable, Static, Type, - PartialHash, } pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( @@ -129,15 +128,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( ty.encode(encoder); poly_trait_ref.encode(encoder); } - GlobalAlloc::Type(ty) => { + GlobalAlloc::Type { ty, segment } => { trace!("encoding {alloc_id:?} with {ty:#?}"); AllocDiscriminant::Type.encode(encoder); ty.encode(encoder); - } - GlobalAlloc::PartialHash(ty) => { - trace!("encoding {alloc_id:?} with {ty:#?}"); - AllocDiscriminant::PartialHash.encode(encoder); - ty.encode(encoder); + segment.encode(encoder); } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); @@ -243,14 +238,9 @@ impl<'s> AllocDecodingSession<'s> { AllocDiscriminant::Type => { trace!("creating typeid alloc ID"); let ty = Decodable::decode(decoder); - trace!("decoded typid: {ty:?}"); - decoder.interner().reserve_and_set_type_id_alloc(ty) - } - AllocDiscriminant::PartialHash => { - trace!("creating typeid alloc ID"); - let ty = Decodable::decode(decoder); - trace!("decoded typid: {ty:?}"); - decoder.interner().reserve_and_set_type_id_partial_hash(ty) + let segment = Decodable::decode(decoder); + trace!("decoded typid: {ty:?} ({segment})"); + decoder.interner().reserve_and_set_type_id_alloc(ty, segment) } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); @@ -282,10 +272,9 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), - /// A pointer to be stored within a TypeId pointing to the full hash. - Type(Ty<'tcx>), - /// A partial type hash (the first `size_of()` bytes of the hash). - PartialHash(Ty<'tcx>), + /// A pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// is split into two segments, on 32 bit systems there are 4 segments, and so on. + Type { ty: Ty<'tcx>, segment: u8 }, } impl<'tcx> GlobalAlloc<'tcx> { @@ -324,8 +313,7 @@ impl<'tcx> GlobalAlloc<'tcx> { pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { match self { GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, - GlobalAlloc::Type(_) - | GlobalAlloc::PartialHash(_) + GlobalAlloc::Type { .. } | GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => AddressSpace::DATA, @@ -364,10 +352,7 @@ impl<'tcx> GlobalAlloc<'tcx> { } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::PartialHash(_) - | GlobalAlloc::Type(_) - | GlobalAlloc::Function { .. } - | GlobalAlloc::VTable(..) => { + GlobalAlloc::Type { .. } | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { // These are immutable. Mutability::Not } @@ -415,8 +400,8 @@ impl<'tcx> GlobalAlloc<'tcx> { // No data to be accessed here. But vtables are pointer-aligned. (Size::ZERO, tcx.data_layout.pointer_align.abi) } - GlobalAlloc::Type(_) => (Size::from_bytes(16), Align::ONE), - GlobalAlloc::PartialHash(_) => (Size::ZERO, Align::ONE), + // Fake allocation, there's nothing to access here + GlobalAlloc::Type { .. } => (Size::ZERO, Align::ONE), } } } @@ -522,15 +507,9 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } - /// Generates an [AllocId] for a [core::mem::type_info::TypeIdData]. Will get deduplicated. - pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::Type(ty), 0) - } - - /// Generates an [AllocId] that will get replaced by a partial hash of a type. - /// See [core::mem::type_info::TypeId] for more information. - pub fn reserve_and_set_type_id_partial_hash(self, ty: Ty<'tcx>) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::PartialHash(ty), 0) + /// Generates an [AllocId] for a [core::mem::type_info::TypeId]. Will get deduplicated. + pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>, segment: u8) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Type { ty, segment }, 0) } /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f5eb7d771dc31..848219dc5b4b2 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1621,8 +1621,9 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(w, " (vtable: impl {dyn_ty} for {ty})")? } - Some(GlobalAlloc::Type(ty)) => write!(w, " (typeid for {ty})")?, - Some(GlobalAlloc::PartialHash(ty)) => write!(w, " (partial hash of {ty})")?, + Some(GlobalAlloc::Type { ty, segment }) => { + write!(w, " (typeid segment {segment} for {ty})")? + } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c582f5e83c65f..b0a69bffe017a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1780,8 +1780,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } Some(GlobalAlloc::Function { .. }) => p!(""), Some(GlobalAlloc::VTable(..)) => p!(""), - Some(GlobalAlloc::Type(_)) => p!(""), - Some(GlobalAlloc::PartialHash(_)) => p!(""), + Some(GlobalAlloc::Type { .. }) => p!(""), None => p!(""), } return Ok(()); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 9212da9a55ee3..76b967c2adbbc 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1227,8 +1227,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt )); collect_alloc(tcx, alloc_id, output) } - GlobalAlloc::Type(_) => {} - GlobalAlloc::PartialHash(_) => {} + GlobalAlloc::Type { .. } => {} } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index fea3dfa3059fe..88eeebf1111d1 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -325,7 +325,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(args); } } - GlobalAlloc::PartialHash(ty) | GlobalAlloc::Type(ty) => self.visit(ty), + GlobalAlloc::Type { ty, .. } => self.visit(ty), GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 834ba019066f0..25832e316b8d3 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -752,8 +752,7 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { GlobalAlloc::Static(tables.static_def(*def)) } mir::interpret::GlobalAlloc::Memory(alloc) => GlobalAlloc::Memory(alloc.stable(tables)), - mir::interpret::GlobalAlloc::Type(_) => todo!(), - mir::interpret::GlobalAlloc::PartialHash(_) => todo!(), + mir::interpret::GlobalAlloc::Type { .. } => todo!(), } } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 5f6fbda58f451..62409e9ee7ec4 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -709,14 +709,7 @@ impl dyn Any + Send + Sync { #[stable(feature = "rust1", since = "1.0.0")] #[lang = "type_id"] pub struct TypeId { - /// Quick accept: if pointers are the same, the ids are the same - data: &'static TypeIdData, - /// Quick reject: if hashes are different, the ids are different - /// We use a raw pointer instead of a `usize`, because const eval - /// will be storing this as a fake pointer that will turn into the - /// appropriate bits at codegen time. This prevents users from inspecting - /// these bits at compile time. - partial_hash: *const (), + data: [*const (); 16 / size_of::()], } // SAFETY: the raw pointer is always an integer @@ -726,11 +719,6 @@ unsafe impl Send for TypeId {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for TypeId {} -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct TypeIdData { - full_hash: [u8; 16], -} - #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] impl const PartialEq for TypeId { @@ -742,8 +730,7 @@ impl const PartialEq for TypeId { #[inline] fn rt(a: &TypeId, b: &TypeId) -> bool { - a.data as *const TypeIdData == b.data as *const TypeIdData - || (a.partial_hash == b.partial_hash && a.data.full_hash == b.data.full_hash) + a.data == b.data } core::intrinsics::const_eval_select((self, other), ct, rt) @@ -757,9 +744,7 @@ impl const PartialEq for TypeId { #[inline] fn rt(a: &TypeId, b: &TypeId) -> bool { - a.partial_hash != b.partial_hash - || (a.data as *const TypeIdData != b.data as *const TypeIdData - && a.data.full_hash != b.data.full_hash) + a.data != b.data } core::intrinsics::const_eval_select((self, other), ct, rt) @@ -789,7 +774,8 @@ impl TypeId { } fn as_u128(self) -> u128 { - u128::from_ne_bytes(self.data.full_hash) + // SAFETY: we know there are 16 bytes without provenance at this location + unsafe { crate::ptr::read_unaligned(&self.data as *const _ as *const u128) } } } @@ -809,12 +795,9 @@ impl hash::Hash for TypeId { // - It is correct to do so -- only hashing a subset of `self` is still // compatible with an `Eq` implementation that considers the entire // value, as ours does. - if cfg!(target_pointer_width = "64") { - self.partial_hash.hash(state); - } else { - let [_, _, _, _, _, _, _, _, data @ ..] = self.data.full_hash; - u64::from_ne_bytes(data).hash(state); - } + // SAFETY: we know there are 16 bytes without provenance at this location + let data = unsafe { crate::ptr::read_unaligned(&self.data as *const _ as *const u64) }; + data.hash(state); } } diff --git a/tests/ui/consts/const_transmute_type_id.rs b/tests/ui/consts/const_transmute_type_id.rs new file mode 100644 index 0000000000000..52cc2853ab821 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id.rs @@ -0,0 +1,11 @@ +#![feature(const_type_id, const_trait_impl)] + +use std::any::TypeId; + +const _: () = { + let id = TypeId::of::(); + let id: u8 = unsafe { (&id as *const TypeId).cast::().read() }; + //~^ ERROR: unable to turn pointer into integer +}; + +fn main() {} diff --git a/tests/ui/consts/const_transmute_type_id.stderr b/tests/ui/consts/const_transmute_type_id.stderr new file mode 100644 index 0000000000000..fafced4d48b97 --- /dev/null +++ b/tests/ui/consts/const_transmute_type_id.stderr @@ -0,0 +1,12 @@ +error[E0080]: unable to turn pointer into integer + --> $DIR/const_transmute_type_id.rs:7:27 + | +LL | let id: u8 = unsafe { (&id as *const TypeId).cast::().read() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From 9d6f08ff6951652334431d7a81509a248e9888b6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 27 Jun 2025 09:52:47 +0000 Subject: [PATCH 11/12] Cursed gvn type_id test --- tests/mir-opt/gvn_type_id_polymorphic.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/mir-opt/gvn_type_id_polymorphic.rs b/tests/mir-opt/gvn_type_id_polymorphic.rs index 39bc5c24ecc68..6285dca9d95ea 100644 --- a/tests/mir-opt/gvn_type_id_polymorphic.rs +++ b/tests/mir-opt/gvn_type_id_polymorphic.rs @@ -6,7 +6,20 @@ fn generic() {} const fn type_id_of_val(_: &T) -> u128 { - std::intrinsics::type_id::() + let name = std::intrinsics::type_name::(); + let len = name.len() as u64; + let len = u64::to_be_bytes(len); + let mut ret = [0; 16]; + let mut i = 0; + while i < 8 { + ret[i] = len[i]; + i += 1; + } + while i < 16 { + ret[i] = name.as_bytes()[i - 8]; + i += 1; + } + u128::from_be_bytes(ret) } // EMIT_MIR gvn_type_id_polymorphic.cursed_is_i32.GVN.diff From 2e8cecebfd487afb0cb6a93d75d27e7c2ed6e952 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 27 Jun 2025 13:37:51 +0000 Subject: [PATCH 12/12] Only mark the first pointer-width bytes of TypeIdwith provenance --- .../rustc_codegen_cranelift/src/constant.rs | 36 +++------------- compiler/rustc_codegen_cranelift/src/lib.rs | 1 - compiler/rustc_codegen_gcc/src/common.rs | 27 ++---------- compiler/rustc_codegen_gcc/src/lib.rs | 1 - compiler/rustc_codegen_llvm/src/common.rs | 31 +++----------- .../src/const_eval/machine.rs | 41 +++++++------------ .../src/interpret/intrinsics.rs | 27 ++++++++---- .../rustc_const_eval/src/interpret/memory.rs | 4 +- .../rustc_middle/src/mir/interpret/mod.rs | 16 ++++---- compiler/rustc_middle/src/mir/pretty.rs | 4 +- library/core/src/any.rs | 27 +----------- library/core/src/intrinsics/mod.rs | 8 +++- 12 files changed, 65 insertions(+), 158 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 5b2459a7d6e8b..864a836bf89d8 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -3,8 +3,6 @@ use std::cmp::Ordering; use cranelift_module::*; -use rustc_abi::Align; -use rustc_const_eval::interpret::{AllocInit, Allocation, alloc_range}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint}; @@ -177,11 +175,11 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - GlobalAlloc::Type { ty: type_id_ty, segment } => { + GlobalAlloc::Type { .. } => { return CValue::const_val( fx, layout, - type_id_segment(fx.tcx, type_id_ty, segment), + ScalarInt::try_from_target_usize(offset.bytes(), fx.tcx).unwrap(), ); } GlobalAlloc::Static(def_id) => { @@ -221,23 +219,6 @@ pub(crate) fn codegen_const_value<'tcx>( } } -fn type_id_segment<'tcx>(tcx: TyCtxt<'tcx>, type_id_ty: Ty<'tcx>, segment: u8) -> ScalarInt { - let type_id = tcx.type_id_hash(type_id_ty).as_u128(); - let mut alloc: Allocation = - Allocation::new(Size::from_bytes(16), Align::from_bytes(8).unwrap(), AllocInit::Uninit, ()); - alloc - .write_scalar( - &tcx, - alloc_range(Size::ZERO, Size::from_bytes(16)), - Scalar::from_u128(type_id), - ) - .unwrap(); - let pointer_size = tcx.data_layout.pointer_size; - let offset = pointer_size * u64::from(segment); - let value = alloc.read_scalar(&tcx, alloc_range(offset, pointer_size), false).unwrap(); - value.to_scalar_int().unwrap() -} - fn pointer_for_allocation<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, alloc_id: AllocId, @@ -498,16 +479,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant .principal() .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), ), - GlobalAlloc::Type { ty, segment } => { - let val = type_id_segment(tcx, ty, segment); - let Init::Bytes { contents } = &mut data.init else { unreachable!() }; - let start = offset.bytes_usize(); - let ptr_size = tcx.data_layout.pointer_size; - let src = val.to_bits(ptr_size); - let src = u128::to_le_bytes(src); - let len = ptr_size.bytes_usize(); - assert!(src[len..].iter().all(|b| *b == 0)); - contents[start..(start + len)].copy_from_slice(&src[..len]); + GlobalAlloc::Type { .. } => { + // Nothing to do, the bytes/offset of this pointer have already been written together with all other bytes, + // so we just need to drop this provenance. continue; } GlobalAlloc::Static(def_id) => { diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 41d0f8ddd7237..8e34436fb5e0a 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -20,7 +20,6 @@ extern crate rustc_middle; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_codegen_ssa; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_fs_util; diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index b2b8127b88933..b1823901723ff 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,10 +1,9 @@ use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, Align, HasDataLayout, Size}; +use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; -use rustc_const_eval::interpret::{AllocInit, Allocation, alloc_range}; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::layout::LayoutOf; @@ -303,28 +302,8 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { let init = self.const_data_from_alloc(alloc); self.static_addr_of(init, alloc.inner().align, None) } - GlobalAlloc::Type { ty: type_id_ty, segment } => { - let type_id = self.tcx.type_id_hash(type_id_ty).as_u128(); - let mut alloc: Allocation = Allocation::new( - Size::from_bytes(16), - Align::from_bytes(8).unwrap(), - AllocInit::Uninit, - (), - ); - alloc - .write_scalar( - &self.tcx, - alloc_range(Size::ZERO, Size::from_bytes(16)), - Scalar::from_u128(type_id), - ) - .unwrap(); - let pointer_size = self.tcx.data_layout.pointer_size; - let offset = pointer_size * u64::from(segment); - let value = alloc - .read_scalar(&self.tcx, alloc_range(offset, pointer_size), false) - .unwrap(); - let data = value.to_bits(pointer_size).unwrap() as u64; - let val = self.const_usize(data); + GlobalAlloc::Type { .. } => { + let val = self.const_usize(offset.bytes()); return self.context.new_cast(None, val, ty); } GlobalAlloc::Static(def_id) => { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index b514010db1a77..a912678ef2a10 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -41,7 +41,6 @@ extern crate rustc_apfloat; extern crate rustc_ast; extern crate rustc_attr_data_structures; extern crate rustc_codegen_ssa; -extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_fluent_macro; diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 105ab667fa4a9..605b136ebd51f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,7 +4,7 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, Align, HasDataLayout as _, Size}; +use rustc_abi::{self as abi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -12,9 +12,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hashes::Hash128; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::mir::interpret::{ - AllocInit, Allocation, ConstAllocation, GlobalAlloc, Scalar, alloc_range, -}; +use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::DllImport; use tracing::debug; @@ -326,28 +324,9 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } - GlobalAlloc::Type { ty, segment } => { - let type_id = self.tcx.type_id_hash(ty).as_u128(); - let mut alloc: Allocation = Allocation::new( - Size::from_bytes(16), - Align::from_bytes(8).unwrap(), - AllocInit::Uninit, - (), - ); - alloc - .write_scalar( - &self.tcx, - alloc_range(Size::ZERO, Size::from_bytes(16)), - Scalar::from_u128(type_id), - ) - .unwrap(); - let pointer_size = self.tcx.data_layout.pointer_size; - let offset = pointer_size * u64::from(segment); - let value = alloc - .read_scalar(&self.tcx, alloc_range(offset, pointer_size), false) - .unwrap(); - let data = value.to_bits(pointer_size).unwrap() as u64; - let llval = self.const_usize(data); + GlobalAlloc::Type { .. } => { + // Drop the provenance, the offset contains the bytes of the hash + let llval = self.const_usize(offset.bytes()); return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; } }; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index b79360af0868c..e0c72cab07d2e 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -406,33 +406,20 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { sym::type_id_eq => { let a = ecx.project_field(&args[0], FieldIdx::ZERO)?; let b = ecx.project_field(&args[1], FieldIdx::ZERO)?; - let mut eq = true; - for index in 0..(16 / ecx.tcx.data_layout.pointer_size.bytes()) { - let a = ecx.project_index(&a, index)?; - let a = ecx.deref_pointer(&a)?; - let (a, offset) = a.ptr().into_parts(); - assert_eq!(offset, Size::ZERO); - let a = a.unwrap().alloc_id(); - let GlobalAlloc::Type { ty: a, segment: a_segment } = ecx.tcx.global_alloc(a) - else { - bug!() - }; - let b = ecx.project_index(&b, index)?; - let b = ecx.deref_pointer(&b)?; - let (b, offset) = b.ptr().into_parts(); - assert_eq!(offset, Size::ZERO); - let b = b.unwrap().alloc_id(); - let GlobalAlloc::Type { ty: b, segment: b_segment } = ecx.tcx.global_alloc(b) - else { - bug!() - }; - - eq &= a == b && a_segment == b_segment; - if !eq { - break; - } - } - ecx.write_scalar(Scalar::from_bool(eq), dest)?; + + let a = ecx.project_index(&a, 0)?; + let a = ecx.deref_pointer(&a)?; + let (a, offset_a) = a.ptr().into_parts(); + let a = a.unwrap().alloc_id(); + let GlobalAlloc::Type { ty: a } = ecx.tcx.global_alloc(a) else { bug!() }; + + let b = ecx.project_index(&b, 0)?; + let b = ecx.deref_pointer(&b)?; + let (b, offset_b) = b.ptr().into_parts(); + let b = b.unwrap().alloc_id(); + let GlobalAlloc::Type { ty: b } = ecx.tcx.global_alloc(b) else { bug!() }; + + ecx.write_scalar(Scalar::from_bool(a == b && offset_a == offset_b), dest)?; } sym::const_allocate => { let size = ecx.read_scalar(&args[0])?.to_target_usize(ecx)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 3005283d7e28d..4a4c20cbc9ca2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -36,13 +36,26 @@ pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { let align = tcx.data_layout.pointer_align; let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ()); let ptr_size = tcx.data_layout.pointer_size; - for step in 0..size.bytes() / ptr_size.bytes() { - let offset = ptr_size * step; - let alloc_id = tcx.reserve_and_set_type_id_alloc(ty, step.try_into().unwrap()); - let ptr = Pointer::new(alloc_id.into(), Size::ZERO); - let val = Scalar::from_pointer(ptr, &tcx); - alloc.write_scalar(&tcx, alloc_range(offset, ptr_size), val).unwrap(); - } + 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; diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6b35071f653ad..10ab0c6b1d0e2 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1211,8 +1211,8 @@ 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::Type { ty, segment }) => { - write!(fmt, " (typeid segment {segment } for {ty})")?; + Some(GlobalAlloc::Type { 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_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 59f8509d1b595..3889cf3b057dd 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -128,11 +128,10 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( ty.encode(encoder); poly_trait_ref.encode(encoder); } - GlobalAlloc::Type { ty, segment } => { + GlobalAlloc::Type { ty } => { trace!("encoding {alloc_id:?} with {ty:#?}"); AllocDiscriminant::Type.encode(encoder); ty.encode(encoder); - segment.encode(encoder); } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); @@ -238,9 +237,8 @@ impl<'s> AllocDecodingSession<'s> { AllocDiscriminant::Type => { trace!("creating typeid alloc ID"); let ty = Decodable::decode(decoder); - let segment = Decodable::decode(decoder); - trace!("decoded typid: {ty:?} ({segment})"); - decoder.interner().reserve_and_set_type_id_alloc(ty, segment) + trace!("decoded typid: {ty:?}"); + decoder.interner().reserve_and_set_type_id_alloc(ty) } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); @@ -272,9 +270,9 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), - /// A pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id /// is split into two segments, on 32 bit systems there are 4 segments, and so on. - Type { ty: Ty<'tcx>, segment: u8 }, + Type { ty: Ty<'tcx> }, } impl<'tcx> GlobalAlloc<'tcx> { @@ -508,8 +506,8 @@ impl<'tcx> TyCtxt<'tcx> { } /// Generates an [AllocId] for a [core::mem::type_info::TypeId]. Will get deduplicated. - pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>, segment: u8) -> AllocId { - self.reserve_and_set_dedup(GlobalAlloc::Type { ty, segment }, 0) + pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Type { ty }, 0) } /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 848219dc5b4b2..6bdbe711f9855 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1621,9 +1621,7 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(w, " (vtable: impl {dyn_ty} for {ty})")? } - Some(GlobalAlloc::Type { ty, segment }) => { - write!(w, " (typeid segment {segment} for {ty})")? - } + Some(GlobalAlloc::Type { ty }) => write!(w, " (typeid for {ty})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 62409e9ee7ec4..bc4bac77ab96f 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -709,7 +709,7 @@ impl dyn Any + Send + Sync { #[stable(feature = "rust1", since = "1.0.0")] #[lang = "type_id"] pub struct TypeId { - data: [*const (); 16 / size_of::()], + pub(crate) data: [*const (); 16 / size_of::()], } // SAFETY: the raw pointer is always an integer @@ -724,30 +724,7 @@ unsafe impl Sync for TypeId {} impl const PartialEq for TypeId { #[inline] fn eq(&self, other: &Self) -> bool { - const fn ct(a: &TypeId, b: &TypeId) -> bool { - crate::intrinsics::type_id_eq(*a, *b) - } - - #[inline] - fn rt(a: &TypeId, b: &TypeId) -> bool { - a.data == b.data - } - - core::intrinsics::const_eval_select((self, other), ct, rt) - } - - #[inline] - fn ne(&self, other: &Self) -> bool { - const fn ct(a: &TypeId, b: &TypeId) -> bool { - !crate::intrinsics::type_id_eq(*a, *b) - } - - #[inline] - fn rt(a: &TypeId, b: &TypeId) -> bool { - a.data != b.data - } - - core::intrinsics::const_eval_select((self, other), ct, rt) + crate::intrinsics::type_id_eq(*self, *other) } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 499e816c10d1f..67a509312e91a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2728,8 +2728,12 @@ pub const fn type_id() -> crate::any::TypeId; #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] -pub const fn type_id_eq(_a: crate::any::TypeId, _b: crate::any::TypeId) -> bool { - panic!("type_id_eq should only be used from const eval") +// The intrinsic is always overridden by CTFE and we don't want to limit the default body to +// const operations only, as that would require transmuting the raw pointers to usize because +// you can't do that cast in CTFE. +#[rustc_do_not_const_check] +pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool { + a.data == b.data } /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`.