Remove the nullary intrinsic const eval logic and treat them like other intrinsics
This commit is contained in:
parent
672e0bec9e
commit
d37a04d09d
6 changed files with 78 additions and 124 deletions
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -841,7 +841,7 @@ pub const unsafe fn transmute_unchecked<Src, Dst>(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<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 to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`].
|
||||
|
|
@ -2696,7 +2696,7 @@ pub const unsafe fn align_of_val<T: ?Sized>(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<T: ?Sized>() -> &'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`].
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue