Auto merge of #142839 - oli-obk:denullarification, r=RalfJung,celinval

Stop backends from needing to support nullary intrinsics

And then remove our infrastructure special casing them. Further improvements can now be done to them by avoiding the intermediate ConstValue step, but let's leave that to follow up work

r? `@RalfJung`
This commit is contained in:
bors 2025-06-30 11:24:47 +00:00
commit ad3b725761
15 changed files with 164 additions and 208 deletions

View file

@ -660,7 +660,7 @@ pub mod intrinsics {
#[rustc_intrinsic]
pub unsafe fn ctlz_nonzero<T>(x: T) -> u32;
#[rustc_intrinsic]
pub fn needs_drop<T: ?::Sized>() -> bool;
pub const fn needs_drop<T: ?::Sized>() -> bool;
#[rustc_intrinsic]
pub fn bitreverse<T>(x: T) -> T;
#[rustc_intrinsic]

View file

@ -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::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
assert!(!intrinsics::needs_drop::<u8>());
assert!(!intrinsics::needs_drop::<[u8]>());
assert!(intrinsics::needs_drop::<NoisyDrop>());
assert!(intrinsics::needs_drop::<NoisyDropUnsized>());
let u8_needs_drop = const { intrinsics::needs_drop::<u8>() };
assert!(!u8_needs_drop);
let slice_needs_drop = const { intrinsics::needs_drop::<[u8]>() };
assert!(!slice_needs_drop);
let noisy_drop = const { intrinsics::needs_drop::<NoisyDrop>() };
assert!(noisy_drop);
let noisy_unsized_drop = const { intrinsics::needs_drop::<NoisyDropUnsized>() };
assert!(noisy_unsized_drop);
Unique { pointer: NonNull(1 as *mut &str), _marker: PhantomData } as Unique<dyn SomeTrait>;

View file

@ -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);

View file

@ -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::<u16>() as u8, 2);
assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8);
assert!(!intrinsics::needs_drop::<u8>());
assert!(!intrinsics::needs_drop::<[u8]>());
assert!(intrinsics::needs_drop::<NoisyDrop>());
assert!(intrinsics::needs_drop::<NoisyDropUnsized>());
assert!(!const { intrinsics::needs_drop::<u8>() });
assert!(!const { intrinsics::needs_drop::<[u8]>() });
assert!(const { intrinsics::needs_drop::<NoisyDrop>() });
assert!(const { intrinsics::needs_drop::<NoisyDropUnsized>() });
Unique {
pointer: 0 as *const &str,

View file

@ -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();
@ -150,10 +171,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);

View file

@ -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 =

View file

@ -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))
}

View file

@ -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

View file

@ -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;

View file

@ -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<T: ?Sized + 'static>() -> TypeId {
let t: u128 = intrinsics::type_id::<T>();
let t: u128 = const { intrinsics::type_id::<T>() };
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<T: ?Sized>() -> &'static str {
intrinsics::type_name::<T>()
const { intrinsics::type_name::<T>() }
}
/// Returns the type name of the pointed-to value as a string slice.

View file

@ -839,10 +839,10 @@ pub const unsafe fn transmute_unchecked<Src, Dst>(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<T>() -> 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<T: ?Sized>(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<T: ?Sized>() -> &'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]

View file

@ -616,7 +616,7 @@ pub const unsafe fn align_of_val_raw<T: ?Sized>(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<T: ?Sized>() -> bool {
intrinsics::needs_drop::<T>()
const { intrinsics::needs_drop::<T>() }
}
/// Returns the value of type `T` represented by the all-zero byte-pattern.
@ -1215,7 +1215,7 @@ pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
#[rustc_const_unstable(feature = "variant_count", issue = "73662")]
#[rustc_diagnostic_item = "mem_variant_count"]
pub const fn variant_count<T>() -> usize {
intrinsics::variant_count::<T>()
const { intrinsics::variant_count::<T>() }
}
/// Provides associated constants for various useful properties of types,

View file

@ -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 {

View file

@ -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<u128> = 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::<char>();
let _bool_id = type_id::<bool>();
let _char_id = const {{ type_id::<char>() }};
let _bool_id = const {{ type_id::<bool>() }};
}}
pub fn main() {{

View file

@ -5,7 +5,7 @@
#![allow(dead_code)]
const fn type_name_wrapper<T>(_: &T) -> &'static str {
core::intrinsics::type_name::<T>()
const { core::intrinsics::type_name::<T>() }
}
struct Struct<TA, TB, TC> {