From 672e0bec9edfb174c93edafcfe46a8fa3c05c931 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 21 Jun 2025 11:26:27 +0000 Subject: [PATCH 1/4] 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 d37a04d09da8edb0d83ee5de30f2fa8db1758a85 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 21 Jun 2025 17:33:55 +0000 Subject: [PATCH 2/4] 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 2a2c3e6aee2af..22a1894ee7208 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -263,9 +263,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 569a07c3a011e..8fddc7e324973 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 b29c5c7c7d7b6..d7cede7129306 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::{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 bd3205c02b05568087ed724de3f878c8f826c622 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 26 Jun 2025 14:53:36 +0000 Subject: [PATCH 3/4] 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 d0bb9a73aa51322476a7efbfc9cd010f788932bb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 27 Jun 2025 10:30:12 +0000 Subject: [PATCH 4/4] 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 {