Add opaque TypeId handles for CTFE
This commit is contained in:
parent
6b3ae3f6e4
commit
486ffda9dc
43 changed files with 438 additions and 148 deletions
|
|
@ -175,6 +175,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)
|
||||
}
|
||||
GlobalAlloc::TypeId { .. } => {
|
||||
return CValue::const_val(
|
||||
fx,
|
||||
layout,
|
||||
ScalarInt::try_from_target_usize(offset.bytes(), fx.tcx).unwrap(),
|
||||
);
|
||||
}
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
assert!(fx.tcx.is_static(def_id));
|
||||
let data_id = data_id_for_static(
|
||||
|
|
@ -360,6 +367,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
GlobalAlloc::Memory(alloc) => alloc,
|
||||
GlobalAlloc::Function { .. }
|
||||
| GlobalAlloc::Static(_)
|
||||
| GlobalAlloc::TypeId { .. }
|
||||
| GlobalAlloc::VTable(..) => {
|
||||
unreachable!()
|
||||
}
|
||||
|
|
@ -471,6 +479,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
|
|||
.principal()
|
||||
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
|
||||
),
|
||||
GlobalAlloc::TypeId { .. } => {
|
||||
// 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) => {
|
||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
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, HasDataLayout};
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
|
|
@ -282,6 +281,13 @@ 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::TypeId { .. } => {
|
||||
let val = self.const_usize(offset.bytes());
|
||||
// This is still a variable of pointer type, even though we only use the provenance
|
||||
// of that pointer in CTFE and Miri. But to make LLVM's type system happy,
|
||||
// we need an int-to-ptr cast here (it doesn't matter at all which provenance that picks).
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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, HasDataLayout as _};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
|
|
@ -284,7 +283,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,15 +316,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
|
|||
}),
|
||||
)))
|
||||
.unwrap_memory();
|
||||
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
|
||||
let value = self.static_addr_of_impl(init, alloc.inner().align, None);
|
||||
value
|
||||
let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false);
|
||||
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::TypeId { .. } => {
|
||||
// Drop the provenance, the offset contains the bytes of the hash
|
||||
let llval = self.const_usize(offset.bytes());
|
||||
return unsafe { llvm::LLVMConstIntToPtr(llval, llty) };
|
||||
}
|
||||
};
|
||||
let base_addr_space = global_alloc.address_space(self);
|
||||
let llval = unsafe {
|
||||
|
|
@ -346,7 +350,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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -141,7 +140,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) {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,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(
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
WriteToReadOnly(_) => const_eval_write_to_read_only,
|
||||
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
|
||||
DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
|
||||
DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer,
|
||||
InvalidBool(_) => const_eval_invalid_bool,
|
||||
InvalidChar(_) => const_eval_invalid_char,
|
||||
InvalidTag(_) => const_eval_invalid_tag,
|
||||
|
|
@ -588,7 +589,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
|||
diag.arg("has", has.bytes());
|
||||
diag.arg("msg", format!("{msg:?}"));
|
||||
}
|
||||
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
|
||||
WriteToReadOnly(alloc)
|
||||
| DerefFunctionPointer(alloc)
|
||||
| DerefVTablePointer(alloc)
|
||||
| DerefTypeIdPointer(alloc) => {
|
||||
diag.arg("allocation", alloc);
|
||||
}
|
||||
InvalidBool(b) => {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
use rustc_abi::Size;
|
||||
use rustc_abi::{FieldIdx, Size};
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range};
|
||||
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
|
|
@ -29,6 +31,37 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
|
|||
tcx.mk_const_alloc(alloc)
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId {
|
||||
let size = Size::from_bytes(16);
|
||||
let align = tcx.data_layout.pointer_align();
|
||||
let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ());
|
||||
let ptr_size = tcx.data_layout.pointer_size();
|
||||
let type_id_hash = tcx.type_id_hash(ty).as_u128();
|
||||
alloc
|
||||
.write_scalar(
|
||||
&tcx,
|
||||
alloc_range(Size::ZERO, Size::from_bytes(16)),
|
||||
Scalar::from_u128(type_id_hash),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Give the first pointer-size bytes provenance that knows about the type id
|
||||
|
||||
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
|
||||
let offset = alloc
|
||||
.read_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), false)
|
||||
.unwrap()
|
||||
.to_target_usize(&tcx)
|
||||
.unwrap();
|
||||
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
|
||||
let val = Scalar::from_pointer(ptr, &tcx);
|
||||
alloc.write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), val).unwrap();
|
||||
|
||||
alloc.mutability = Mutability::Not;
|
||||
|
||||
tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc))
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Returns `true` if emulation happened.
|
||||
/// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
|
||||
|
|
@ -63,10 +96,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
sym::type_id => {
|
||||
let tp_ty = instance.args.type_at(0);
|
||||
ensure_monomorphic_enough(tcx, tp_ty)?;
|
||||
let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128());
|
||||
let alloc_id = alloc_type_id(tcx, tp_ty);
|
||||
let val = ConstValue::Indirect { alloc_id, offset: Size::ZERO };
|
||||
let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
|
||||
self.copy_op(&val, dest)?;
|
||||
}
|
||||
sym::type_id_eq => {
|
||||
// Both operands are `TypeId`, which is a newtype around an array of pointers.
|
||||
// Project until we have the array elements.
|
||||
let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
|
||||
let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
|
||||
|
||||
let mut a_fields = self.project_array_fields(&a_fields)?;
|
||||
let mut b_fields = self.project_array_fields(&b_fields)?;
|
||||
|
||||
let (_idx, a) = a_fields
|
||||
.next(self)?
|
||||
.expect("we know the layout of TypeId has at least 2 array elements");
|
||||
let a = self.deref_pointer(&a)?;
|
||||
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
|
||||
|
||||
let (_idx, b) = b_fields
|
||||
.next(self)?
|
||||
.expect("we know the layout of TypeId has at least 2 array elements");
|
||||
let b = self.deref_pointer(&b)?;
|
||||
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
|
||||
|
||||
let provenance_matches = a == b;
|
||||
|
||||
let mut eq_id = offset_a == offset_b;
|
||||
|
||||
while let Some((_, a)) = a_fields.next(self)? {
|
||||
let (_, b) = b_fields.next(self)?.unwrap();
|
||||
|
||||
let a = self.read_target_usize(&a)?;
|
||||
let b = self.read_target_usize(&b)?;
|
||||
eq_id &= a == b;
|
||||
}
|
||||
|
||||
if !eq_id && provenance_matches {
|
||||
throw_ub_format!(
|
||||
"type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
|
||||
)
|
||||
}
|
||||
|
||||
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
|
||||
}
|
||||
sym::variant_count => {
|
||||
let tp_ty = instance.args.type_at(0);
|
||||
let ty = match tp_ty.kind() {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ use std::{fmt, ptr};
|
|||
use rustc_abi::{Align, HasDataLayout, Size};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::display_allocation;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, throw_ub_format};
|
||||
use tracing::{debug, instrument, trace};
|
||||
|
||||
use super::{
|
||||
|
|
@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
kind = "vtable",
|
||||
)
|
||||
}
|
||||
Some(GlobalAlloc::TypeId { .. }) => {
|
||||
err_ub_custom!(
|
||||
fluent::const_eval_invalid_dealloc,
|
||||
alloc_id = alloc_id,
|
||||
kind = "typeid",
|
||||
)
|
||||
}
|
||||
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
|
||||
err_ub_custom!(
|
||||
fluent::const_eval_invalid_dealloc,
|
||||
|
|
@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
|
||||
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
|
||||
Some(GlobalAlloc::TypeId { .. }) => throw_ub!(DerefTypeIdPointer(id)),
|
||||
None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
assert!(self.tcx.is_static(def_id));
|
||||
|
|
@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
|
||||
let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
|
||||
let kind = match global_alloc {
|
||||
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
|
||||
GlobalAlloc::TypeId { .. }
|
||||
| GlobalAlloc::Static { .. }
|
||||
| GlobalAlloc::Memory { .. } => AllocKind::LiveData,
|
||||
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
|
||||
GlobalAlloc::VTable { .. } => AllocKind::VTable,
|
||||
};
|
||||
|
|
@ -936,6 +946,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Takes a pointer that is the first chunk of a `TypeId` and return the type that its
|
||||
/// provenance refers to, as well as the segment of the hash that this pointer covers.
|
||||
pub fn get_ptr_type_id(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
|
||||
let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
|
||||
let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
|
||||
throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
|
||||
};
|
||||
interp_ok((ty, offset))
|
||||
}
|
||||
|
||||
pub fn get_ptr_fn(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
|
|
@ -1197,6 +1220,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
|
|||
Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
|
||||
write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?;
|
||||
}
|
||||
Some(GlobalAlloc::TypeId { ty }) => {
|
||||
write!(fmt, " (typeid for {ty})")?;
|
||||
}
|
||||
Some(GlobalAlloc::Static(did)) => {
|
||||
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,7 +296,11 @@ where
|
|||
base: &'a P,
|
||||
) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> {
|
||||
let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
|
||||
span_bug!(self.cur_span(), "project_array_fields: expected an array layout");
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"project_array_fields: expected an array layout, got {:#?}",
|
||||
base.layout()
|
||||
);
|
||||
};
|
||||
let len = base.len(self)?;
|
||||
let field_layout = base.layout().field(self, 0);
|
||||
|
|
|
|||
|
|
@ -571,40 +571,42 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
let alloc_actual_mutbl =
|
||||
global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env);
|
||||
|
||||
if let GlobalAlloc::Static(did) = global_alloc {
|
||||
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
|
||||
bug!()
|
||||
};
|
||||
// Special handling for pointers to statics (irrespective of their type).
|
||||
assert!(!self.ecx.tcx.is_thread_local_static(did));
|
||||
assert!(self.ecx.tcx.is_static(did));
|
||||
// Mode-specific checks
|
||||
match ctfe_mode {
|
||||
CtfeValidationMode::Static { .. }
|
||||
| CtfeValidationMode::Promoted { .. } => {
|
||||
// We skip recursively checking other statics. These statics must be sound by
|
||||
// themselves, and the only way to get broken statics here is by using
|
||||
// unsafe code.
|
||||
// The reasons we don't check other statics is twofold. For one, in all
|
||||
// sound cases, the static was already validated on its own, and second, we
|
||||
// trigger cycle errors if we try to compute the value of the other static
|
||||
// and that static refers back to us (potentially through a promoted).
|
||||
// This could miss some UB, but that's fine.
|
||||
// We still walk nested allocations, as they are fundamentally part of this validation run.
|
||||
// This means we will also recurse into nested statics of *other*
|
||||
// statics, even though we do not recurse into other statics directly.
|
||||
// That's somewhat inconsistent but harmless.
|
||||
skip_recursive_check = !nested;
|
||||
}
|
||||
CtfeValidationMode::Const { .. } => {
|
||||
// If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
|
||||
// just get errors trying to read the value.
|
||||
if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did)
|
||||
{
|
||||
skip_recursive_check = true;
|
||||
match global_alloc {
|
||||
GlobalAlloc::Static(did) => {
|
||||
let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else {
|
||||
bug!()
|
||||
};
|
||||
assert!(!self.ecx.tcx.is_thread_local_static(did));
|
||||
assert!(self.ecx.tcx.is_static(did));
|
||||
match ctfe_mode {
|
||||
CtfeValidationMode::Static { .. }
|
||||
| CtfeValidationMode::Promoted { .. } => {
|
||||
// We skip recursively checking other statics. These statics must be sound by
|
||||
// themselves, and the only way to get broken statics here is by using
|
||||
// unsafe code.
|
||||
// The reasons we don't check other statics is twofold. For one, in all
|
||||
// sound cases, the static was already validated on its own, and second, we
|
||||
// trigger cycle errors if we try to compute the value of the other static
|
||||
// and that static refers back to us (potentially through a promoted).
|
||||
// This could miss some UB, but that's fine.
|
||||
// We still walk nested allocations, as they are fundamentally part of this validation run.
|
||||
// This means we will also recurse into nested statics of *other*
|
||||
// statics, even though we do not recurse into other statics directly.
|
||||
// That's somewhat inconsistent but harmless.
|
||||
skip_recursive_check = !nested;
|
||||
}
|
||||
CtfeValidationMode::Const { .. } => {
|
||||
// If this is mutable memory or an `extern static`, there's no point in checking it -- we'd
|
||||
// just get errors trying to read the value.
|
||||
if alloc_actual_mutbl.is_mut()
|
||||
|| self.ecx.tcx.is_foreign_item(did)
|
||||
{
|
||||
skip_recursive_check = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// If this allocation has size zero, there is no actual mutability here.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -220,7 +221,13 @@ 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::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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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::TypeId { 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 = <DefId as Decodable<D>>::decode(decoder);
|
||||
|
|
@ -258,6 +270,9 @@ pub enum GlobalAlloc<'tcx> {
|
|||
Static(DefId),
|
||||
/// The alloc ID points to memory.
|
||||
Memory(ConstAllocation<'tcx>),
|
||||
/// 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.
|
||||
TypeId { ty: 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::ZERO
|
||||
}
|
||||
GlobalAlloc::TypeId { .. }
|
||||
| GlobalAlloc::Static(..)
|
||||
| GlobalAlloc::Memory(..)
|
||||
| GlobalAlloc::VTable(..) => AddressSpace::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +350,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
|
|||
}
|
||||
}
|
||||
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
|
||||
GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
|
||||
GlobalAlloc::TypeId { .. } | 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)
|
||||
}
|
||||
// Fake allocation, there's nothing to access here
|
||||
GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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::any::TypeId]. Will get deduplicated.
|
||||
pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::TypeId { 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.,
|
||||
|
|
|
|||
|
|
@ -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::TypeId { 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)
|
||||
|
|
|
|||
|
|
@ -1773,6 +1773,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
}
|
||||
Some(GlobalAlloc::Function { .. }) => p!("<function>"),
|
||||
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
|
||||
Some(GlobalAlloc::TypeId { .. }) => p!("<typeid>"),
|
||||
None => p!("<dangling pointer>"),
|
||||
}
|
||||
return Ok(());
|
||||
|
|
|
|||
|
|
@ -1219,6 +1219,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
|
|||
));
|
||||
collect_alloc(tcx, alloc_id, output)
|
||||
}
|
||||
GlobalAlloc::TypeId { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
self.visit(args);
|
||||
}
|
||||
}
|
||||
GlobalAlloc::TypeId { ty, .. } => self.visit(ty),
|
||||
GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2194,6 +2194,7 @@ symbols! {
|
|||
type_changing_struct_update,
|
||||
type_const,
|
||||
type_id,
|
||||
type_id_eq,
|
||||
type_ir,
|
||||
type_ir_infer_ctxt_like,
|
||||
type_ir_inherent,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ pub enum GlobalAlloc {
|
|||
Static(StaticDef),
|
||||
/// The alloc ID points to memory.
|
||||
Memory(Allocation),
|
||||
/// 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.
|
||||
TypeId { ty: Ty },
|
||||
}
|
||||
|
||||
impl From<AllocId> for GlobalAlloc {
|
||||
|
|
|
|||
|
|
@ -864,6 +864,9 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> {
|
|||
mir::interpret::GlobalAlloc::Memory(alloc) => {
|
||||
GlobalAlloc::Memory(alloc.stable(tables, cx))
|
||||
}
|
||||
mir::interpret::GlobalAlloc::TypeId { ty } => {
|
||||
GlobalAlloc::TypeId { ty: ty.stable(tables, cx) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -707,19 +707,52 @@ impl dyn Any + Send + Sync {
|
|||
/// ```
|
||||
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[lang = "type_id"]
|
||||
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),
|
||||
#[cfg(feature = "debug_typeid")]
|
||||
name: &'static str,
|
||||
/// This needs to be an array of pointers, since there is provenance
|
||||
/// in the first array field. This provenance knows exactly which type
|
||||
/// the TypeId actually is, allowing CTFE and miri to operate based off it.
|
||||
/// At runtime all the pointers in the array contain bits of the hash, making
|
||||
/// the entire `TypeId` actually just be a `u128` hash of the type.
|
||||
pub(crate) data: [*const (); 16 / size_of::<*const ()>()],
|
||||
}
|
||||
|
||||
// SAFETY: the raw pointer is always an integer
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialEq for TypeId {
|
||||
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 {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
|
||||
impl const PartialEq for TypeId {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.t == other.t
|
||||
#[cfg(miri)]
|
||||
return crate::intrinsics::type_id_eq(*self, *other);
|
||||
#[cfg(not(miri))]
|
||||
{
|
||||
let this = self;
|
||||
crate::intrinsics::const_eval_select!(
|
||||
@capture { this: &TypeId, other: &TypeId } -> bool:
|
||||
if const {
|
||||
crate::intrinsics::type_id_eq(*this, *other)
|
||||
} else {
|
||||
// Ideally we would just invoke `type_id_eq` unconditionally here,
|
||||
// but since we do not MIR inline intrinsics, because backends
|
||||
// may want to override them (and miri does!), MIR opts do not
|
||||
// clean up this call sufficiently for LLVM to turn repeated calls
|
||||
// of `TypeId` comparisons against one specific `TypeId` into
|
||||
// a lookup table.
|
||||
// SAFETY: We know that at runtime none of the bits have provenance and all bits
|
||||
// are initialized. So we can just convert the whole thing to a `u128` and compare that.
|
||||
unsafe {
|
||||
crate::mem::transmute::<_, u128>(*this) == crate::mem::transmute::<_, u128>(*other)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -742,19 +775,19 @@ 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 = const { intrinsics::type_id::<T>() };
|
||||
let t1 = (t >> 64) as u64;
|
||||
let t2 = t as u64;
|
||||
|
||||
TypeId {
|
||||
t: (t1, t2),
|
||||
#[cfg(feature = "debug_typeid")]
|
||||
name: type_name::<T>(),
|
||||
}
|
||||
const { intrinsics::type_id::<T>() }
|
||||
}
|
||||
|
||||
fn as_u128(self) -> u128 {
|
||||
u128::from(self.t.0) << 64 | u128::from(self.t.1)
|
||||
let mut bytes = [0; 16];
|
||||
|
||||
// This is a provenance-stripping memcpy.
|
||||
for (i, chunk) in self.data.iter().copied().enumerate() {
|
||||
let chunk = chunk.expose_provenance().to_ne_bytes();
|
||||
let start = i * chunk.len();
|
||||
bytes[start..(start + chunk.len())].copy_from_slice(&chunk);
|
||||
}
|
||||
u128::from_ne_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -774,22 +807,19 @@ 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);
|
||||
let data =
|
||||
// SAFETY: The `offset` stays in-bounds, it just moves the pointer to the 2nd half of the `TypeId`.
|
||||
// Only the first ptr-sized chunk ever has provenance, so that second half is always
|
||||
// fine to read at integer type.
|
||||
unsafe { crate::ptr::read_unaligned(self.data.as_ptr().cast::<u64>().offset(1)) };
|
||||
data.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2724,7 +2724,20 @@ pub const fn type_name<T: ?Sized>() -> &'static str;
|
|||
#[rustc_nounwind]
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn type_id<T: ?Sized + 'static>() -> u128;
|
||||
pub const fn type_id<T: ?Sized + 'static>() -> 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]
|
||||
#[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`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ impl std::error::Error for MyError {
|
|||
// and eliminate redundant ones, rather than compare one-by-one.
|
||||
|
||||
// CHECK-NEXT: start:
|
||||
// CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i64, ptr
|
||||
// CHECK-NEXT: switch i64 %[[SCRUTINEE]], label %{{.*}} [
|
||||
// CHECK-COUNT-3: i64 {{.*}}, label %{{.*}}
|
||||
// CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i128, ptr
|
||||
// CHECK-NEXT: switch i128 %[[SCRUTINEE]], label %{{.*}} [
|
||||
// CHECK-COUNT-3: i128 {{.*}}, label %{{.*}}
|
||||
// CHECK-NEXT: ]
|
||||
request
|
||||
.provide_ref::<MyBacktrace1>(&self.backtrace1)
|
||||
|
|
|
|||
|
|
@ -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<T: 'static>(_val: T)
|
|||
where
|
||||
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
|
||||
//~^ ERROR overly complex generic constant
|
||||
//~| ERROR: cannot call
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +21,6 @@ fn test<T: 'static>()
|
|||
where
|
||||
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
|
||||
//~^ ERROR overly complex generic constant
|
||||
//~| ERROR: cannot call
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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::<T>() != TypeId::of::<()>() }>: True,
|
||||
| ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
@ -20,26 +20,5 @@ LL | If<{ TypeId::of::<T>() != 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::<T>() != 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 2 previous errors
|
||||
|
||||
error[E0015]: cannot call non-const operator in constants
|
||||
--> $DIR/issue-90318.rs:22:10
|
||||
|
|
||||
LL | If<{ TypeId::of::<T>() != 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
|
||||
|
||||
For more information about this error, try `rustc --explain E0015`.
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ use std::any::TypeId;
|
|||
fn main() {
|
||||
const {
|
||||
assert!(TypeId::of::<u8>() == TypeId::of::<u8>());
|
||||
//~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied
|
||||
assert!(TypeId::of::<()>() != TypeId::of::<u8>());
|
||||
//~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied
|
||||
let _a = TypeId::of::<u8>() < TypeId::of::<u16>();
|
||||
//~^ ERROR: cannot call non-const operator in constants
|
||||
// can't assert `_a` because it is not deterministic
|
||||
// FIXME(const_trait_impl) make it pass
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<u8>() == TypeId::of::<u8>());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied
|
||||
--> $DIR/const_cmp_type_id.rs:10:17
|
||||
LL | let _a = TypeId::of::<u8>() < TypeId::of::<u16>();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
LL | assert!(TypeId::of::<()>() != TypeId::of::<u8>());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
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`.
|
||||
|
|
|
|||
11
tests/ui/consts/const_transmute_type_id.rs
Normal file
11
tests/ui/consts/const_transmute_type_id.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#![feature(const_type_id, const_trait_impl)]
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
const _: () = {
|
||||
let id = TypeId::of::<u8>();
|
||||
let id: u8 = unsafe { (&raw const id).cast::<u8>().read() };
|
||||
//~^ ERROR: unable to turn pointer into integer
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
12
tests/ui/consts/const_transmute_type_id.stderr
Normal file
12
tests/ui/consts/const_transmute_type_id.stderr
Normal file
|
|
@ -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 { (&raw const id).cast::<u8>().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`.
|
||||
14
tests/ui/consts/const_transmute_type_id2.rs
Normal file
14
tests/ui/consts/const_transmute_type_id2.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ normalize-stderr: "0x(ff)+" -> "<u128::MAX>"
|
||||
|
||||
#![feature(const_type_id, const_trait_impl)]
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
const _: () = {
|
||||
let a: TypeId = unsafe { std::mem::transmute(u128::MAX) };
|
||||
let b: TypeId = unsafe { std::mem::transmute(u128::MAX) };
|
||||
assert!(a == b);
|
||||
//~^ ERROR: pointer must point to some allocation
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
15
tests/ui/consts/const_transmute_type_id2.stderr
Normal file
15
tests/ui/consts/const_transmute_type_id2.stderr
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got <u128::MAX>[noalloc] which is a dangling pointer (it has no provenance)
|
||||
--> $DIR/const_transmute_type_id2.rs:10:13
|
||||
|
|
||||
LL | assert!(a == b);
|
||||
| ^^^^^^ evaluation of `_` failed inside this call
|
||||
|
|
||||
note: inside `<TypeId as PartialEq>::eq`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
note: inside `<TypeId as PartialEq>::eq::compiletime`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
= note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
16
tests/ui/consts/const_transmute_type_id3.rs
Normal file
16
tests/ui/consts/const_transmute_type_id3.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#![feature(const_type_id, const_trait_impl)]
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
const _: () = {
|
||||
let a = TypeId::of::<()>();
|
||||
let mut b = TypeId::of::<()>();
|
||||
unsafe {
|
||||
let ptr = &mut b as *mut TypeId as *mut usize;
|
||||
std::ptr::write(ptr.offset(1), 999);
|
||||
}
|
||||
assert!(a == b);
|
||||
//~^ ERROR: one of the TypeId arguments is invalid, the hash does not match the type it represents
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
15
tests/ui/consts/const_transmute_type_id3.stderr
Normal file
15
tests/ui/consts/const_transmute_type_id3.stderr
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
error[E0080]: type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents
|
||||
--> $DIR/const_transmute_type_id3.rs:12:13
|
||||
|
|
||||
LL | assert!(a == b);
|
||||
| ^^^^^^ evaluation of `_` failed inside this call
|
||||
|
|
||||
note: inside `<TypeId as PartialEq>::eq`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
note: inside `<TypeId as PartialEq>::eq::compiletime`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
= note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
16
tests/ui/consts/const_transmute_type_id4.rs
Normal file
16
tests/ui/consts/const_transmute_type_id4.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#![feature(const_type_id, const_trait_impl)]
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
const _: () = {
|
||||
let a = TypeId::of::<()>();
|
||||
let mut b = TypeId::of::<()>();
|
||||
unsafe {
|
||||
let ptr = &mut b as *mut TypeId as *mut *const ();
|
||||
std::ptr::write(ptr.offset(0), main as fn() as *const ());
|
||||
}
|
||||
assert!(a == b);
|
||||
//~^ ERROR: type_id_eq: `TypeId` provenance is not a type id
|
||||
};
|
||||
|
||||
fn main() {}
|
||||
15
tests/ui/consts/const_transmute_type_id4.stderr
Normal file
15
tests/ui/consts/const_transmute_type_id4.stderr
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
error[E0080]: type_id_eq: `TypeId` provenance is not a type id
|
||||
--> $DIR/const_transmute_type_id4.rs:12:13
|
||||
|
|
||||
LL | assert!(a == b);
|
||||
| ^^^^^^ evaluation of `_` failed inside this call
|
||||
|
|
||||
note: inside `<TypeId as PartialEq>::eq`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
note: inside `<TypeId as PartialEq>::eq::compiletime`
|
||||
--> $SRC_DIR/core/src/any.rs:LL:COL
|
||||
= note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
error[E0277]: the trait bound `TypeId: [const] PartialEq` is not satisfied
|
||||
--> $DIR/issue-73976-monomorphic.rs:21:5
|
||||
|
|
||||
LL | GetTypeId::<T>::VALUE == GetTypeId::<usize>::VALUE
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue