Auto merge of #99420 - RalfJung:vtable, r=oli-obk

make vtable pointers entirely opaque

This implements the scheme discussed in https://github.com/rust-lang/unsafe-code-guidelines/issues/338: vtable pointers should be considered entirely opaque and not even readable by Rust code, similar to function pointers.

- We have a new kind of `GlobalAlloc` that symbolically refers to a vtable.
- Miri uses that kind of allocation when generating a vtable.
- The codegen backends, upon encountering such an allocation, call `vtable_allocation` to obtain an actually dataful allocation for this vtable.
- We need new intrinsics to obtain the size and align from a vtable (for some `ptr::metadata` APIs), since direct accesses are UB now.

I had to touch quite a bit of code that I am not very familiar with, so some of this might not make much sense...
r? `@oli-obk`
This commit is contained in:
bors 2022-07-22 01:33:49 +00:00
commit aa01891700
69 changed files with 673 additions and 527 deletions

View File

@ -195,9 +195,8 @@ pub(crate) fn codegen_const_value<'tcx>(
}
Scalar::Ptr(ptr, _size) => {
let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
let alloc_kind = fx.tcx.get_global_alloc(alloc_id);
let base_addr = match alloc_kind {
Some(GlobalAlloc::Memory(alloc)) => {
let base_addr = match fx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Memory(alloc) => {
let data_id = data_id_for_alloc_id(
&mut fx.constants_cx,
fx.module,
@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>(
}
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
Some(GlobalAlloc::Function(instance)) => {
GlobalAlloc::Function(instance) => {
let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
let local_func_id =
fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
}
Some(GlobalAlloc::Static(def_id)) => {
GlobalAlloc::VTable(ty, trait_ref) => {
let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref));
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
// FIXME: factor this common code with the `Memory` arm into a function?
let data_id = data_id_for_alloc_id(
&mut fx.constants_cx,
fx.module,
alloc_id,
alloc.inner().mutability,
);
let local_data_id =
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
GlobalAlloc::Static(def_id) => {
assert!(fx.tcx.is_static(def_id));
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
let local_data_id =
@ -227,7 +240,6 @@ pub(crate) fn codegen_const_value<'tcx>(
}
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
None => bug!("missing allocation {:?}", alloc_id),
};
let val = if offset.bytes() != 0 {
fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap())
@ -357,10 +369,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
while let Some(todo_item) = cx.todo.pop() {
let (data_id, alloc, section_name) = match todo_item {
TodoItem::Alloc(alloc_id) => {
//println!("alloc_id {}", alloc_id);
let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
let alloc = match tcx.global_alloc(alloc_id) {
GlobalAlloc::Memory(alloc) => alloc,
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => {
unreachable!()
}
};
let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
module
@ -424,7 +437,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
read_target_uint(endianness, bytes).unwrap()
};
let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap();
let reloc_target_alloc = tcx.global_alloc(alloc_id);
let data_id = match reloc_target_alloc {
GlobalAlloc::Function(instance) => {
assert_eq!(addend, 0);
@ -436,6 +449,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
GlobalAlloc::Memory(target_alloc) => {
data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
}
GlobalAlloc::VTable(ty, trait_ref) => {
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
}
GlobalAlloc::Static(def_id) => {
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
{

View File

@ -431,6 +431,16 @@ fn codegen_regular_intrinsic_call<'tcx>(
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
};
vtable_size, (v vtable) {
let size = crate::vtable::size_of_obj(fx, vtable);
ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
};
vtable_align, (v vtable) {
let align = crate::vtable::min_align_of_obj(fx, vtable);
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
};
unchecked_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem
| unchecked_shl | unchecked_shr, (c x, c y) {
// FIXME trap on overflow

View File

@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
GlobalAlloc::Function(fn_instance) => {
self.get_fn_addr(fn_instance)
},
GlobalAlloc::VTable(ty, trait_ref) => {
let alloc = self.tcx.global_alloc(self.tcx.vtable_allocation((ty, trait_ref))).unwrap_memory();
let init = const_alloc_to_gcc(self, alloc);
self.static_addr_of(init, alloc.inner().align, None)
}
GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id));
self.get_static(def_id).get_address(None)

View File

@ -257,6 +257,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
self.get_fn_addr(fn_instance.polymorphize(self.tcx)),
self.data_layout().instruction_address_space,
),
GlobalAlloc::VTable(ty, trait_ref) => {
let alloc = self
.tcx
.global_alloc(self.tcx.vtable_allocation((ty, trait_ref)))
.unwrap_memory();
let init = const_alloc_to_llvm(self, alloc);
let value = self.static_addr_of(init, alloc.inner().align, None);
(value, AddressSpace::DATA)
}
GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id));
assert!(!self.tcx.is_thread_local_static(def_id));

View File

@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
let address_space = match cx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA,
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
AddressSpace::DATA
}
};
llvals.push(cx.scalar_to_backend(

View File

@ -1420,7 +1420,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
cx,
type_map::stub(
cx,
Stub::VtableTy { vtable_holder },
Stub::VTableTy { vtable_holder },
unique_type_id,
&vtable_type_name,
(size, pointer_align),

View File

@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> {
pub enum Stub<'ll> {
Struct,
Union,
VtableTy { vtable_holder: &'ll DIType },
VTableTy { vtable_holder: &'ll DIType },
}
pub struct StubInfo<'ll, 'tcx> {
@ -180,9 +180,9 @@ pub(super) fn stub<'ll, 'tcx>(
let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
let metadata = match kind {
Stub::Struct | Stub::VtableTy { .. } => {
Stub::Struct | Stub::VTableTy { .. } => {
let vtable_holder = match kind {
Stub::VtableTy { vtable_holder } => Some(vtable_holder),
Stub::VTableTy { vtable_holder } => Some(vtable_holder),
_ => None,
};
unsafe {

View File

@ -171,7 +171,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
);
let new_vptr = bx.load(ptr_ty, gep, ptr_align);
bx.nonnull_metadata(new_vptr);
// Vtable loads are invariant.
// VTable loads are invariant.
bx.set_invariant_load(new_vptr);
new_vptr
} else {

View File

@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex {
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
let ptr = bx.load(llty, gep, ptr_align);
bx.nonnull_metadata(ptr);
// Vtable loads are invariant.
// VTable loads are invariant.
bx.set_invariant_load(ptr);
ptr
}
@ -58,7 +58,7 @@ impl<'a, 'tcx> VirtualIndex {
let usize_align = bx.tcx().data_layout.pointer_align.abi;
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
let ptr = bx.load(llty, gep, usize_align);
// Vtable loads are invariant.
// VTable loads are invariant.
bx.set_invariant_load(ptr);
ptr
}

View File

@ -3,12 +3,16 @@ use super::place::PlaceRef;
use super::FunctionCx;
use crate::common::{span_invalid_monomorphization_error, IntPredicate};
use crate::glue;
use crate::meth;
use crate::traits::*;
use crate::MemFlags;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{sym, Span};
use rustc_target::abi::call::{FnAbi, PassMode};
use rustc_target::abi::{
call::{FnAbi, PassMode},
WrappingRange,
};
fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
@ -102,6 +106,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes())
}
}
sym::vtable_size | sym::vtable_align => {
let vtable = args[0].immediate();
let idx = match name {
sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE,
sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
_ => bug!(),
};
let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable);
if name == sym::vtable_align {
// Alignment is always nonzero.
bx.range_metadata(value, WrappingRange { start: 1, end: !0 });
};
value
}
sym::pref_align_of
| sym::needs_drop
| sym::type_id

View File

@ -369,7 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
// we don't deallocate it.
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?;
let is_allocated_in_another_const = matches!(
ecx.tcx.get_global_alloc(alloc_id),
ecx.tcx.try_get_global_alloc(alloc_id),
Some(interpret::GlobalAlloc::Memory(_))
);

View File

@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
let mplace = ecx.deref_operand(&op).unwrap();
if let Some(alloc_id) = mplace.ptr.provenance {
assert_eq!(
tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability,
tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
Mutability::Not,
"deref_mir_constant cannot be used with mutable allocations as \
that could allow pattern matching to observe mutable statics",

View File

@ -298,30 +298,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.write_immediate(val, dest)
}
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let val = self.read_immediate(src)?;
if data_a.principal_def_id() == data_b.principal_def_id() {
return self.write_immediate(*val, dest);
}
// trait upcasting coercion
let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
src_pointee_ty,
dest_pointee_ty,
));
if let Some(entry_idx) = vptr_entry_idx {
let entry_idx = u64::try_from(entry_idx).unwrap();
let (old_data, old_vptr) = val.to_scalar_pair()?;
let old_vptr = self.scalar_to_ptr(old_vptr)?;
let new_vptr = self
.read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
} else {
self.write_immediate(*val, dest)
let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?;
let old_vptr = self.scalar_to_ptr(old_vptr)?;
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
if old_trait != data_a.principal() {
throw_ub_format!("upcast on a pointer whose vtable does not match its type");
}
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
}
(_, &ty::Dynamic(ref data, _)) => {
// Initial cast from sized to dyn trait
let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
let ptr = self.read_immediate(src)?.to_scalar()?;
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
self.write_immediate(val, dest)

View File

@ -631,7 +631,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty::Dynamic(..) => {
let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?;
// Read size and align from vtable (already checks size).
Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
Ok(Some(self.get_vtable_size_and_align(vtable)?))
}
ty::Slice(_) | ty::Str => {

View File

@ -94,7 +94,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval:
// to validation to error -- it has the much better error messages, pointing out where
// in the value the dangling reference lies.
// The `delay_span_bug` ensures that we don't forget such a check in validation.
if tcx.get_global_alloc(alloc_id).is_none() {
if tcx.try_get_global_alloc(alloc_id).is_none() {
tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
}
// treat dangling pointers like other statics
@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive<
.sess
.span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
return Err(reported);
} else if ecx.tcx.get_global_alloc(alloc_id).is_none() {
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
// We have hit an `AllocId` that is neither in local or global memory and isn't
// marked as dangling by local memory. That should be impossible.
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);

View File

@ -492,6 +492,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
self.write_scalar(result, dest)?;
}
sym::vtable_size => {
let ptr = self.read_pointer(&args[0])?;
let (size, _align) = self.get_vtable_size_and_align(ptr)?;
self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?;
}
sym::vtable_align => {
let ptr = self.read_pointer(&args[0])?;
let (_size, align) = self.get_vtable_size_and_align(ptr)?;
self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?;
}
_ => return Ok(false),
}

View File

@ -16,7 +16,7 @@ use std::ptr;
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::display_allocation;
use rustc_middle::ty::{Instance, ParamEnv, TyCtxt};
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Align, HasDataLayout, Size};
use super::{
@ -62,6 +62,8 @@ pub enum AllocKind {
LiveData,
/// A function allocation (that fn ptrs point to).
Function,
/// A (symbolic) vtable allocation.
VTable,
/// A dead allocation.
Dead,
}
@ -159,7 +161,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let alloc_id = ptr.provenance;
// We need to handle `extern static`.
match self.tcx.get_global_alloc(alloc_id) {
match self.tcx.try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
bug!("global memory cannot point to thread-local static")
}
@ -287,10 +289,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
// Deallocating global memory -- always an error
return Err(match self.tcx.get_global_alloc(alloc_id) {
return Err(match self.tcx.try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::Function(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is a function")
}
Some(GlobalAlloc::VTable(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is a vtable")
}
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is static memory")
}
@ -473,12 +478,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
id: AllocId,
is_write: bool,
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> {
let (alloc, def_id) = match self.tcx.get_global_alloc(id) {
let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Memory(mem)) => {
// Memory of a constant or promoted or anonymous memory referenced by a static.
(mem, None)
}
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
None => throw_ub!(PointerUseAfterFree(id)),
Some(GlobalAlloc::Static(def_id)) => {
assert!(self.tcx.is_static(def_id));
@ -494,6 +500,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// contains a reference to memory that was created during its evaluation (i.e., not
// to another static), those inner references only exist in "resolved" form.
if self.tcx.is_foreign_item(def_id) {
// This is unreachable in Miri, but can happen in CTFE where we actually *do* support
// referencing arbitrary (declared) extern statics.
throw_unsup!(ReadExternStatic(def_id));
}
@ -663,12 +671,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// # Statics
// Can't do this in the match argument, we may get cycle errors since the lock would
// be held throughout the match.
match self.tcx.get_global_alloc(id) {
Some(GlobalAlloc::Static(did)) => {
assert!(!self.tcx.is_thread_local_static(did));
match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Static(def_id)) => {
assert!(self.tcx.is_static(def_id));
assert!(!self.tcx.is_thread_local_static(def_id));
// Use size and align of the type.
let ty = self.tcx.type_of(did);
let ty = self.tcx.type_of(def_id);
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
assert!(!layout.is_unsized());
(layout.size, layout.align.abi, AllocKind::LiveData)
}
Some(GlobalAlloc::Memory(alloc)) => {
@ -678,6 +688,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(alloc.size(), alloc.align, AllocKind::LiveData)
}
Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
Some(GlobalAlloc::VTable(..)) => {
// No data to be accessed here. But vtables are pointer-aligned.
return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable);
}
// The rest must be dead.
None => {
// Deallocated pointers are allowed, we should be able to find
@ -705,7 +719,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) {
Some(FnVal::Other(*extra))
} else {
match self.tcx.get_global_alloc(id) {
match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)),
_ => None,
}
@ -716,7 +730,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&self,
ptr: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
trace!("get_fn({:?})", ptr);
trace!("get_ptr_fn({:?})", ptr);
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 {
throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
@ -725,6 +739,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into())
}
pub fn get_ptr_vtable(
&self,
ptr: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)> {
trace!("get_ptr_vtable({:?})", ptr);
let (alloc_id, offset, _tag) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 {
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
}
match self.tcx.try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::VTable(ty, trait_ref)) => Ok((ty, trait_ref)),
_ => throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))),
}
}
pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not;
Ok(())
@ -829,7 +858,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
}
None => {
// global alloc
match self.ecx.tcx.get_global_alloc(id) {
match self.ecx.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Memory(alloc)) => {
write!(fmt, " (unchanged global, ")?;
write_allocation_track_relocs(
@ -840,7 +869,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
)?;
}
Some(GlobalAlloc::Function(func)) => {
write!(fmt, " (fn: {})", func)?;
write!(fmt, " (fn: {func})")?;
}
Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
write!(fmt, " (vtable: impl {trait_ref} for {ty})")?;
}
Some(GlobalAlloc::VTable(ty, None)) => {
write!(fmt, " (vtable: impl <auto trait> for {ty})")?;
}
Some(GlobalAlloc::Static(did)) => {
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;

View File

@ -885,28 +885,19 @@ where
}
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
/// Also return some more information so drop doesn't have to run the same code twice.
pub(super) fn unpack_dyn_trait(
&self,
mplace: &MPlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::Provenance>)> {
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
let vtable = self.scalar_to_ptr(mplace.vtable())?; // also sanity checks the type
let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
let (ty, _) = self.get_ptr_vtable(vtable)?;
let layout = self.layout_of(ty)?;
// More sanity checks
if cfg!(debug_assertions) {
let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
assert_eq!(size, layout.size);
// only ABI alignment is preserved
assert_eq!(align, layout.align.abi);
}
let mplace = MPlaceTy {
mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace },
layout,
align: layout.align.abi,
};
Ok((instance, mplace))
Ok(mplace)
}
}

View File

@ -1,5 +1,4 @@
use std::borrow::Cow;
use std::convert::TryFrom;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::Instance;
@ -365,7 +364,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
M::call_intrinsic(self, instance, args, destination, target, unwind)
}
ty::InstanceDef::VtableShim(..)
ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::FnPtrShim(..)
@ -520,7 +519,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
// cannot use the shim here, because that will only result in infinite recursion
ty::InstanceDef::Virtual(_, idx) => {
ty::InstanceDef::Virtual(def_id, idx) => {
let mut args = args.to_vec();
// We have to implement all "object safe receivers". So we have to go search for a
// pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
@ -553,17 +552,53 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
};
// Find and consult vtable. The type now could be something like RcBox<dyn Trait>,
// i.e., it is still not necessarily `ty::Dynamic` (so we cannot use
// `place.vtable()`), but it should have a `dyn Trait` tail.
assert!(matches!(
self.tcx
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env)
.kind(),
ty::Dynamic(..)
));
let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?;
// Obtain the underlying trait we are working on.
let receiver_tail = self
.tcx
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
let ty::Dynamic(data, ..) = receiver_tail.kind() else {
span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
};
// Get the required information from the vtable.
let vptr = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
throw_ub_format!(
"`dyn` call on a pointer whose vtable does not match its type"
);
}
// Now determine the actual method to call. We can do that in two different ways and
// compare them to ensure everything fits.
let ty::VtblEntry::Method(fn_inst) = self.get_vtable_entries(vptr)?[idx] else {
span_bug!(self.cur_span(), "dyn call index points at something that is not a method")
};
if cfg!(debug_assertions) {
let tcx = *self.tcx;
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
let virtual_trait_ref =
ty::TraitRef::from_method(tcx, trait_def_id, instance.substs);
assert_eq!(
receiver_tail,
virtual_trait_ref.self_ty(),
"mismatch in underlying dyn trait computation within Miri and MIR building",
);
let existential_trait_ref =
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
let concrete_method = Instance::resolve(
tcx,
self.param_env,
def_id,
instance.substs.rebase_onto(tcx, trait_def_id, concrete_trait_ref.substs),
)
.unwrap()
.unwrap();
assert_eq!(fn_inst, concrete_method);
}
// `*mut receiver_place.layout.ty` is almost the layout that we
// want for args[0]: We have to project to field 0 because we want
@ -579,7 +614,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
trace!("Patched receiver operand to {:#?}", args[0]);
// recurse with concrete function
self.eval_fn_call(
fn_val,
FnVal::Instance(fn_inst),
(caller_abi, caller_fn_abi),
&args,
with_caller_location,
@ -606,8 +641,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (instance, place) = match place.layout.ty.kind() {
ty::Dynamic(..) => {
// Dropping a trait object.
self.unpack_dyn_trait(&place)?
// Dropping a trait object. Need to find actual drop fn.
let place = self.unpack_dyn_trait(&place)?;
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
(instance, place)
}
_ => (instance, place),
};

View File

@ -1,24 +1,21 @@
use std::convert::TryFrom;
use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic};
use rustc_middle::ty::{
self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE,
COMMON_VTABLE_ENTRIES_SIZE,
};
use rustc_middle::mir::interpret::{InterpResult, Pointer};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_target::abi::{Align, Size};
use super::util::ensure_monomorphic_enough;
use super::{FnVal, InterpCx, Machine};
use super::{InterpCx, Machine};
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
/// objects.
///
/// The `trait_ref` encodes the erased self type. Hence, if we are
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
/// `trait_ref` would map `T: Trait`.
pub fn get_vtable(
&mut self,
/// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>`
/// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that
/// this is an auto trait without any methods, so we only need the basic vtable (drop, size,
/// align).
pub fn get_vtable_ptr(
&self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
@ -30,114 +27,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ensure_monomorphic_enough(*self.tcx, ty)?;
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref));
let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?;
let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref);
let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?;
Ok(vtable_ptr.into())
}
/// Resolves the function at the specified slot in the provided
/// vtable. Currently an index of '3' (`TyCtxt::COMMON_VTABLE_ENTRIES.len()`)
/// corresponds to the first method declared in the trait of the provided vtable.
pub fn get_vtable_slot(
/// Returns a high-level representation of the entires of the given vtable.
pub fn get_vtable_entries(
&self,
vtable: Pointer<Option<M::Provenance>>,
idx: u64,
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
let ptr_size = self.pointer_size();
let vtable_slot = vtable.offset(ptr_size * idx, self)?;
let vtable_slot = self
.get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
.expect("cannot be a ZST");
let fn_ptr = self.scalar_to_ptr(vtable_slot.read_pointer(Size::ZERO)?.check_init()?)?;
self.get_ptr_fn(fn_ptr)
) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> {
let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?;
Ok(if let Some(poly_trait_ref) = poly_trait_ref {
let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
let trait_ref = self.tcx.erase_regions(trait_ref);
self.tcx.vtable_entries(trait_ref)
} else {
TyCtxt::COMMON_VTABLE_ENTRIES
})
}
/// Returns the drop fn instance as well as the actual dynamic type.
pub fn read_drop_type_from_vtable(
&self,
vtable: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
let pointer_size = self.pointer_size();
// We don't care about the pointee type; we just want a pointer.
let vtable = self
.get_ptr_alloc(
vtable,
pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(),
self.tcx.data_layout.pointer_align.abi,
)?
.expect("cannot be a ZST");
let drop_fn = vtable
.read_pointer(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap())?
.check_init()?;
// We *need* an instance here, no other kind of function value, to be able
// to determine the type.
let drop_instance = self.get_ptr_fn(self.scalar_to_ptr(drop_fn)?)?.as_instance()?;
trace!("Found drop fn: {:?}", drop_instance);
let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
// The drop function takes `*mut T` where `T` is the type being dropped, so get that.
let args = fn_sig.inputs();
if args.len() != 1 {
throw_ub!(InvalidVtableDropFn(fn_sig));
}
let ty =
args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty;
Ok((drop_instance, ty))
}
pub fn read_size_and_align_from_vtable(
pub fn get_vtable_size_and_align(
&self,
vtable: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, (Size, Align)> {
let pointer_size = self.pointer_size();
// We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
// the size, and the align (which we read below).
let vtable = self
.get_ptr_alloc(
vtable,
pointer_size * u64::try_from(TyCtxt::COMMON_VTABLE_ENTRIES.len()).unwrap(),
self.tcx.data_layout.pointer_align.abi,
)?
.expect("cannot be a ZST");
let size = vtable
.read_integer(alloc_range(
pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap(),
pointer_size,
))?
.check_init()?;
let size = size.to_machine_usize(self)?;
let size = Size::from_bytes(size);
let align = vtable
.read_integer(alloc_range(
pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap(),
pointer_size,
))?
.check_init()?;
let align = align.to_machine_usize(self)?;
let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;
if size > self.max_size_of_val() {
throw_ub!(InvalidVtableSize);
}
Ok((size, align))
}
pub fn read_new_vtable_after_trait_upcasting_from_vtable(
&self,
vtable: Pointer<Option<M::Provenance>>,
idx: u64,
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
let pointer_size = self.pointer_size();
let vtable_slot = vtable.offset(pointer_size * idx, self)?;
let new_vtable = self
.get_ptr_alloc(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
.expect("cannot be a ZST");
let new_vtable = self.scalar_to_ptr(new_vtable.read_pointer(Size::ZERO)?.check_init()?)?;
Ok(new_vtable)
let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?;
let layout = self.layout_of(ty)?;
assert!(!layout.is_unsized(), "there are no vtables for unsized types");
Ok((layout.size, layout.align.abi))
}
}

View File

@ -313,50 +313,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
match tail.kind() {
ty::Dynamic(..) => {
let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?;
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!(
self.ecx.check_ptr_access_align(
vtable,
3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
self.ecx.tcx.data_layout.pointer_align.abi,
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
),
// Make sure it is a genuine vtable pointer.
let (_ty, _trait) = try_validation!(
self.ecx.get_ptr_vtable(vtable),
self.path,
err_ub!(DanglingIntPointer(..)) |
err_ub!(PointerUseAfterFree(..)) =>
{ "dangling vtable pointer in wide pointer" },
err_ub!(AlignmentCheckFailed { .. }) =>
{ "unaligned vtable pointer in wide pointer" },
err_ub!(PointerOutOfBounds { .. }) =>
{ "too small vtable" },
err_ub!(InvalidVTablePointer(..)) =>
{ "{vtable}" } expected { "a vtable pointer" },
);
try_validation!(
self.ecx.read_drop_type_from_vtable(vtable),
self.path,
err_ub!(DanglingIntPointer(..)) |
err_ub!(InvalidFunctionPointer(..)) =>
{ "invalid drop function pointer in vtable (not pointing to a function)" },
err_ub!(InvalidVtableDropFn(..)) =>
{ "invalid drop function pointer in vtable (function has incompatible signature)" },
// Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123.
// (We assume there are no other MachineStop errors possible here.)
InterpError::MachineStop(_) =>
{ "vtable pointer does not have permission to read drop function pointer" },
);
try_validation!(
self.ecx.read_size_and_align_from_vtable(vtable),
self.path,
err_ub!(InvalidVtableSize) =>
{ "invalid vtable: size is bigger than largest supported object" },
err_ub!(InvalidVtableAlignment(msg)) =>
{ "invalid vtable: alignment {}", msg },
err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
// Stacked Borrows errors can happen here, see https://github.com/rust-lang/miri/issues/2123.
// (We assume there are no other MachineStop errors possible here.)
InterpError::MachineStop(_) =>
{ "vtable pointer does not have permission to read size and alignment" },
);
// FIXME: More checks for the vtable.
// FIXME: check if the type/trait match what ty::Dynamic says?
}
ty::Slice(..) | ty::Str => {
let _len = try_validation!(
@ -447,7 +412,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// `!` is a ZST and we want to validate it.
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
// Special handling for pointers to statics (irrespective of their type).
let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id);
let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id);
if let Some(GlobalAlloc::Static(did)) = alloc_kind {
assert!(!self.ecx.tcx.is_thread_local_static(did));
assert!(self.ecx.tcx.is_static(did));
@ -607,11 +572,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let _fn = try_validation!(
self.ecx.get_ptr_fn(ptr),
self.path,
err_ub!(DanglingIntPointer(0, _)) =>
{ "a null function pointer" },
err_ub!(DanglingIntPointer(..)) |
err_ub!(InvalidFunctionPointer(..)) =>
{ "{:x}", value } expected { "a function pointer" },
{ "{ptr}" } expected { "a function pointer" },
);
// FIXME: Check if the signature matches
} else {

View File

@ -425,7 +425,7 @@ macro_rules! make_value_visitor {
// unsized values are never immediate, so we can assert_mem_place
let op = v.to_op_for_read(self.ecx())?;
let dest = op.assert_mem_place();
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1;
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?;
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));

View File

@ -1,7 +1,7 @@
use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
use crate::mir::interpret::ConstValue;
use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree};
use crate::ty::{layout, query::TyCtxtAt, tls, Ty, ValTree};
use rustc_data_structures::sync::Lock;
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
@ -219,7 +219,7 @@ pub struct ScalarSizeMismatch {
}
/// Error information for when the program caused Undefined Behavior.
pub enum UndefinedBehaviorInfo<'tcx> {
pub enum UndefinedBehaviorInfo {
/// Free-form case. Only for errors that are never caught!
Ub(String),
/// Unreachable code was executed.
@ -241,12 +241,6 @@ pub enum UndefinedBehaviorInfo<'tcx> {
PointerArithOverflow,
/// Invalid metadata in a wide pointer (using `str` to avoid allocations).
InvalidMeta(&'static str),
/// Invalid drop function in vtable.
InvalidVtableDropFn(FnSig<'tcx>),
/// Invalid size in a vtable: too large.
InvalidVtableSize,
/// Invalid alignment in a vtable: too large, or not a power of 2.
InvalidVtableAlignment(String),
/// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed.
@ -271,6 +265,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
WriteToReadOnly(AllocId),
// Trying to access the data behind a function pointer.
DerefFunctionPointer(AllocId),
// Trying to access the data behind a vtable pointer.
DerefVTablePointer(AllocId),
/// The value validity check found a problem.
/// Should only be thrown by `validity.rs` and always point out which part of the value
/// is the problem.
@ -288,6 +284,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
InvalidTag(Scalar),
/// Using a pointer-not-to-a-function as function pointer.
InvalidFunctionPointer(Pointer),
/// Using a pointer-not-to-a-vtable as vtable pointer.
InvalidVTablePointer(Pointer),
/// Using a string that is not valid UTF-8,
InvalidStr(std::str::Utf8Error),
/// Using uninitialized data where it is not allowed.
@ -300,7 +298,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
UninhabitedEnumVariantWritten,
}
impl fmt::Display for UndefinedBehaviorInfo<'_> {
impl fmt::Display for UndefinedBehaviorInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use UndefinedBehaviorInfo::*;
match self {
@ -315,14 +313,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
InvalidVtableDropFn(sig) => write!(
f,
"invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type",
),
InvalidVtableSize => {
write!(f, "invalid vtable: size is bigger than largest supported object")
}
InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"),
UnterminatedCString(p) => write!(
f,
"reading a null-terminated string starting at {p:?} with no null found before end of allocation",
@ -359,6 +349,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
),
WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
ValidationFailure { path: None, msg } => {
write!(f, "constructing invalid value: {msg}")
}
@ -375,6 +366,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
InvalidFunctionPointer(p) => {
write!(f, "using {p:?} as function pointer but it does not point to a function")
}
InvalidVTablePointer(p) => {
write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
}
InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
InvalidUninitBytes(Some((alloc, info))) => write!(
f,
@ -494,7 +488,7 @@ impl dyn MachineStopType {
pub enum InterpError<'tcx> {
/// The program caused undefined behavior.
UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
UndefinedBehavior(UndefinedBehaviorInfo),
/// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo),

View File

@ -196,6 +196,7 @@ impl fmt::Debug for AllocId {
enum AllocDiscriminant {
Alloc,
Fn,
VTable,
Static,
}
@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
AllocDiscriminant::Fn.encode(encoder);
fn_instance.encode(encoder);
}
GlobalAlloc::VTable(ty, poly_trait_ref) => {
trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
AllocDiscriminant::VTable.encode(encoder);
ty.encode(encoder);
poly_trait_ref.encode(encoder);
}
GlobalAlloc::Static(did) => {
assert!(!tcx.is_thread_local_static(did));
// References to statics doesn't need to know about their allocations,
@ -305,7 +312,9 @@ impl<'s> AllocDecodingSession<'s> {
State::InProgress(TinyList::new_single(self.session_id), alloc_id);
Some(alloc_id)
}
AllocDiscriminant::Fn | AllocDiscriminant::Static => {
AllocDiscriminant::Fn
| AllocDiscriminant::Static
| AllocDiscriminant::VTable => {
// Fns and statics cannot be cyclic, and their `AllocId`
// is determined later by interning.
*entry =
@ -355,6 +364,16 @@ impl<'s> AllocDecodingSession<'s> {
let alloc_id = decoder.interner().create_fn_alloc(instance);
alloc_id
}
AllocDiscriminant::VTable => {
assert!(alloc_id.is_none());
trace!("creating vtable alloc ID");
let ty = <Ty<'_> as Decodable<D>>::decode(decoder);
let poly_trait_ref =
<Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
alloc_id
}
AllocDiscriminant::Static => {
assert!(alloc_id.is_none());
trace!("creating extern static alloc ID");
@ -380,6 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
pub enum GlobalAlloc<'tcx> {
/// The alloc ID is used as a function pointer.
Function(Instance<'tcx>),
/// This alloc ID points to a symbolic (not-reified) vtable.
VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
/// The alloc ID points to a "lazy" static variable that did not get computed (yet).
/// This is also used to break the cycle in recursive statics.
Static(DefId),
@ -407,6 +428,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
_ => bug!("expected function, got {:?}", self),
}
}
/// Panics if the `GlobalAlloc` is not `GlobalAlloc::VTable`
#[track_caller]
#[inline]
pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
match *self {
GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref),
_ => bug!("expected vtable, got {:?}", self),
}
}
}
pub(crate) struct AllocMap<'tcx> {
@ -454,12 +485,12 @@ impl<'tcx> TyCtxt<'tcx> {
}
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
/// Should only be used for function pointers and statics, we don't want
/// to dedup IDs for "real" memory!
/// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
/// don't want to dedup IDs for "real" memory!
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
let mut alloc_map = self.alloc_map.lock();
match alloc {
GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {}
GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {}
GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"),
}
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) {
@ -504,6 +535,15 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
pub fn create_vtable_alloc(
self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> AllocId {
self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref))
}
/// 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.,
@ -521,7 +561,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// This function exists to allow const eval to detect the difference between evaluation-
/// local dangling pointers and allocations in constants/statics.
#[inline]
pub fn get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
self.alloc_map.lock().alloc_map.get(&id).cloned()
}
@ -532,7 +572,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// ids), this function is frequently used throughout rustc, but should not be used within
/// the miri engine.
pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> {
match self.get_global_alloc(id) {
match self.try_get_global_alloc(id) {
Some(alloc) => alloc,
None => bug!("could not find allocation for {id:?}"),
}

View File

@ -362,7 +362,7 @@ impl<'tcx> CodegenUnit<'tcx> {
// the codegen tests and can even make item order
// unstable.
InstanceDef::Item(def) => def.did.as_local().map(Idx::index),
InstanceDef::VtableShim(..)
InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..)
| InstanceDef::Intrinsic(..)
| InstanceDef::FnPtrShim(..)

View File

@ -720,11 +720,17 @@ pub fn write_allocations<'tcx>(
write!(w, "{}", display_allocation(tcx, alloc.inner()))
};
write!(w, "\n{id:?}")?;
match tcx.get_global_alloc(id) {
match tcx.try_get_global_alloc(id) {
// This can't really happen unless there are bugs, but it doesn't cost us anything to
// gracefully handle it and allow buggy rustc to be debugged via allocation printing.
None => write!(w, " (deallocated)")?,
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
write!(w, " (vtable: impl {trait_ref} for {ty})")?
}
Some(GlobalAlloc::VTable(ty, None)) => {
write!(w, " (vtable: impl <auto trait> for {ty})")?
}
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
match tcx.eval_static_initializer(did) {
Ok(alloc) => {

View File

@ -394,7 +394,7 @@ macro_rules! make_mir_visitor {
ty::InstanceDef::Item(_def_id) => {}
ty::InstanceDef::Intrinsic(_def_id) |
ty::InstanceDef::VtableShim(_def_id) |
ty::InstanceDef::VTableShim(_def_id) |
ty::InstanceDef::ReifyShim(_def_id) |
ty::InstanceDef::Virtual(_def_id, _) |
ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |

View File

@ -523,4 +523,5 @@ impl_binder_encode_decode! {
ty::ExistentialPredicate<'tcx>,
ty::TraitRef<'tcx>,
Vec<ty::GeneratorInteriorTypeCause<'tcx>>,
ty::ExistentialTraitRef<'tcx>,
}

View File

@ -147,7 +147,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
ty::tls::with_opt(|tcx| {
trace!("hashing {:?}", *self);
let tcx = tcx.expect("can't hash AllocIds during hir lowering");
tcx.get_global_alloc(*self).hash_stable(hcx, hasher);
tcx.try_get_global_alloc(*self).hash_stable(hcx, hasher);
});
}
}

View File

@ -49,7 +49,7 @@ pub enum InstanceDef<'tcx> {
///
/// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` -
/// and dereference the argument to call the original function.
VtableShim(DefId),
VTableShim(DefId),
/// `fn()` pointer where the function itself cannot be turned into a pointer.
///
@ -145,7 +145,7 @@ impl<'tcx> InstanceDef<'tcx> {
pub fn def_id(self) -> DefId {
match self {
InstanceDef::Item(def) => def.did,
InstanceDef::VtableShim(def_id)
InstanceDef::VTableShim(def_id)
| InstanceDef::ReifyShim(def_id)
| InstanceDef::FnPtrShim(def_id, _)
| InstanceDef::Virtual(def_id, _)
@ -161,7 +161,7 @@ impl<'tcx> InstanceDef<'tcx> {
match self {
ty::InstanceDef::Item(def) => Some(def.did),
ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id),
InstanceDef::VtableShim(..)
InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..)
| InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..)
@ -176,7 +176,7 @@ impl<'tcx> InstanceDef<'tcx> {
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
match self {
InstanceDef::Item(def) => def,
InstanceDef::VtableShim(def_id)
InstanceDef::VTableShim(def_id)
| InstanceDef::ReifyShim(def_id)
| InstanceDef::FnPtrShim(def_id, _)
| InstanceDef::Virtual(def_id, _)
@ -273,7 +273,7 @@ impl<'tcx> InstanceDef<'tcx> {
| InstanceDef::Intrinsic(..)
| InstanceDef::ReifyShim(..)
| InstanceDef::Virtual(..)
| InstanceDef::VtableShim(..) => true,
| InstanceDef::VTableShim(..) => true,
}
}
}
@ -290,7 +290,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
match self.def {
InstanceDef::Item(_) => Ok(()),
InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"),
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
@ -434,7 +434,7 @@ impl<'tcx> Instance<'tcx> {
&& tcx.generics_of(def_id).has_self;
if is_vtable_shim {
debug!(" => associated item with unsizeable self: Self");
Some(Instance { def: InstanceDef::VtableShim(def_id), substs })
Some(Instance { def: InstanceDef::VTableShim(def_id), substs })
} else {
Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| {
match resolved.def {

View File

@ -2771,7 +2771,7 @@ impl<'tcx> ty::Instance<'tcx> {
_ => unreachable!(),
};
if let ty::InstanceDef::VtableShim(..) = self.def {
if let ty::InstanceDef::VTableShim(..) = self.def {
// Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
sig = sig.map_bound(|mut sig| {
let mut inputs_and_output = sig.inputs_and_output.to_vec();

View File

@ -2128,7 +2128,7 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
}
ty::InstanceDef::VtableShim(..)
ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::Intrinsic(..)
| ty::InstanceDef::FnPtrShim(..)

View File

@ -1269,7 +1269,7 @@ pub trait PrettyPrinter<'tcx>:
if let ty::Array(elem, len) = inner.kind() {
if let ty::Uint(ty::UintTy::U8) = elem.kind() {
if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.kind() {
match self.tcx().get_global_alloc(alloc_id) {
match self.tcx().try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::Memory(alloc)) => {
let len = int.assert_bits(self.tcx().data_layout.pointer_size);
let range =
@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>:
p!("<too short allocation>")
}
}
// FIXME: for statics and functions, we could in principle print more detail.
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
Some(GlobalAlloc::Static(def_id)) => {
p!(write("<static({:?})>", def_id))
}
Some(GlobalAlloc::Function(_)) => p!("<function>"),
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
None => p!("<dangling pointer>"),
}
return Ok(self);
@ -1297,7 +1298,8 @@ pub trait PrettyPrinter<'tcx>:
ty::FnPtr(_) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings"
// printing above (which also has to handle pointers to all sorts of things).
if let Some(GlobalAlloc::Function(instance)) = self.tcx().get_global_alloc(alloc_id)
if let Some(GlobalAlloc::Function(instance)) =
self.tcx().try_get_global_alloc(alloc_id)
{
self = self.typed_value(
|this| this.print_value_path(instance.def_id(), instance.substs),

View File

@ -624,7 +624,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> {
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
match self {
ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)),
ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)),
ty::InstanceDef::VTableShim(def_id) => Some(ty::InstanceDef::VTableShim(def_id)),
ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)),
ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)),
ty::InstanceDef::FnPtrShim(def_id, ty) => {
@ -927,7 +927,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> {
substs: self.substs.try_fold_with(folder)?,
def: match self.def {
Item(def) => Item(def.try_fold_with(folder)?),
VtableShim(did) => VtableShim(did.try_fold_with(folder)?),
VTableShim(did) => VTableShim(did.try_fold_with(folder)?),
ReifyShim(did) => ReifyShim(did.try_fold_with(folder)?),
Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?),
FnPtrShim(did, ty) => {
@ -954,7 +954,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> {
self.substs.visit_with(visitor)?;
match self.def {
Item(def) => def.visit_with(visitor),
VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
VTableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => {
did.visit_with(visitor)
}
FnPtrShim(did, ty) | CloneShim(did, ty) => {

View File

@ -246,7 +246,7 @@ impl<'tcx> Inliner<'tcx> {
// not get any optimizations run on it. Any subsequent inlining may cause cycles, but we
// do not need to catch this here, we can wait until the inliner decides to continue
// inlining a second time.
InstanceDef::VtableShim(_)
InstanceDef::VTableShim(_)
| InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }

View File

@ -79,7 +79,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
// These have MIR and if that MIR is inlined, substituted and then inlining is run
// again, a function item can end up getting inlined. Thus we'll be able to cause
// a cycle that way
InstanceDef::VtableShim(_)
InstanceDef::VTableShim(_)
| InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }

View File

@ -32,7 +32,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
let mut result = match instance {
ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
ty::InstanceDef::VtableShim(def_id) => {
ty::InstanceDef::VTableShim(def_id) => {
build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
}
ty::InstanceDef::FnPtrShim(def_id, ty) => {
@ -113,7 +113,7 @@ enum Adjustment {
/// We get passed `&[mut] self` and call the target with `*self`.
///
/// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it
/// (for `VtableShim`, which effectively is passed `&own Self`).
/// (for `VTableShim`, which effectively is passed `&own Self`).
Deref,
/// We get passed `self: Self` and call the target with `&mut self`.
@ -569,7 +569,7 @@ fn build_call_shim<'tcx>(
// FIXME(eddyb) avoid having this snippet both here and in
// `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?).
if let ty::InstanceDef::VtableShim(..) = instance {
if let ty::InstanceDef::VTableShim(..) = instance {
// Modify fn(self, ...) to fn(self: *mut Self, ...)
let mut inputs_and_output = sig.inputs_and_output.to_vec();
let self_arg = &mut inputs_and_output[0];

View File

@ -25,7 +25,7 @@
//! codegen unit:
//!
//! - Constants
//! - Vtables
//! - VTables
//! - Object Shims
//!
//!
@ -992,7 +992,7 @@ fn visit_instance_use<'tcx>(
}
}
ty::InstanceDef::DropGlue(_, Some(_))
| ty::InstanceDef::VtableShim(..)
| ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::Item(..)
@ -1427,6 +1427,10 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte
output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP));
}
}
GlobalAlloc::VTable(ty, trait_ref) => {
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
collect_miri(tcx, alloc_id, output)
}
}
}

View File

@ -271,7 +271,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
MonoItem::Fn(instance) => {
let def_id = match instance.def {
ty::InstanceDef::Item(def) => def.did,
ty::InstanceDef::VtableShim(..)
ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
@ -425,7 +425,7 @@ fn mono_item_visibility<'tcx>(
InstanceDef::DropGlue(def_id, Some(_)) => def_id,
// These are all compiler glue and such, never exported, always hidden.
InstanceDef::VtableShim(..)
InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..)
| InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..)

View File

@ -37,7 +37,7 @@ mod values;
use self::values::Value;
pub use rustc_query_system::query::QueryConfig;
pub(crate) use rustc_query_system::query::{QueryDescription, QueryVtable};
pub(crate) use rustc_query_system::query::{QueryDescription, QueryVTable};
mod on_disk_cache;
pub use on_disk_cache::OnDiskCache;

View File

@ -340,11 +340,11 @@ macro_rules! define_queries {
#[inline]
fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) ->
QueryVtable<QueryCtxt<$tcx>, Self::Key, Self::Value>
QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value>
{
let compute = get_provider!([$($modifiers)*][tcx, $name, key]);
let cache_on_disk = Self::cache_on_disk(tcx.tcx, key);
QueryVtable {
QueryVTable {
anon: is_anon!([$($modifiers)*]),
eval_always: is_eval_always!([$($modifiers)*]),
dep_kind: dep_graph::DepKind::$name,

View File

@ -19,7 +19,7 @@ pub trait QueryConfig {
type Stored: Clone;
}
pub struct QueryVtable<CTX: QueryContext, K, V> {
pub struct QueryVTable<CTX: QueryContext, K, V> {
pub anon: bool,
pub dep_kind: CTX::DepKind,
pub eval_always: bool,
@ -31,7 +31,7 @@ pub struct QueryVtable<CTX: QueryContext, K, V> {
pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>,
}
impl<CTX: QueryContext, K, V> QueryVtable<CTX, K, V> {
impl<CTX: QueryContext, K, V> QueryVTable<CTX, K, V> {
pub(crate) fn to_dep_node(&self, tcx: CTX::DepContext, key: &K) -> DepNode<CTX::DepKind>
where
K: crate::dep_graph::DepNodeParams<CTX::DepContext>,
@ -69,7 +69,7 @@ pub trait QueryDescription<CTX: QueryContext>: QueryConfig {
CTX: 'a;
// Don't use this method to compute query results, instead use the methods on TyCtxt
fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVtable<CTX, Self::Key, Self::Value>;
fn make_vtable(tcx: CTX, key: &Self::Key) -> QueryVTable<CTX, Self::Key, Self::Value>;
fn cache_on_disk(tcx: CTX::DepContext, key: &Self::Key) -> bool;
}

View File

@ -12,7 +12,7 @@ pub use self::caches::{
};
mod config;
pub use self::config::{QueryConfig, QueryDescription, QueryVtable};
pub use self::config::{QueryConfig, QueryDescription, QueryVTable};
use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};

View File

@ -4,7 +4,7 @@
use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
use crate::query::caches::QueryCache;
use crate::query::config::{QueryDescription, QueryVtable};
use crate::query::config::{QueryDescription, QueryVTable};
use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame};
use rustc_data_structures::fingerprint::Fingerprint;
@ -331,7 +331,7 @@ fn try_execute_query<CTX, C>(
span: Span,
key: C::Key,
dep_node: Option<DepNode<CTX::DepKind>>,
query: &QueryVtable<CTX, C::Key, C::Value>,
query: &QueryVTable<CTX, C::Key, C::Value>,
) -> (C::Stored, Option<DepNodeIndex>)
where
C: QueryCache,
@ -368,7 +368,7 @@ fn execute_job<CTX, K, V>(
tcx: CTX,
key: K,
mut dep_node_opt: Option<DepNode<CTX::DepKind>>,
query: &QueryVtable<CTX, K, V>,
query: &QueryVTable<CTX, K, V>,
job_id: QueryJobId,
) -> (V, DepNodeIndex)
where
@ -437,7 +437,7 @@ fn try_load_from_disk_and_cache_in_memory<CTX, K, V>(
tcx: CTX,
key: &K,
dep_node: &DepNode<CTX::DepKind>,
query: &QueryVtable<CTX, K, V>,
query: &QueryVTable<CTX, K, V>,
) -> Option<(V, DepNodeIndex)>
where
K: Clone,
@ -530,7 +530,7 @@ fn incremental_verify_ich<CTX, K, V: Debug>(
tcx: CTX::DepContext,
result: &V,
dep_node: &DepNode<CTX::DepKind>,
query: &QueryVtable<CTX, K, V>,
query: &QueryVTable<CTX, K, V>,
) where
CTX: QueryContext,
{
@ -642,7 +642,7 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
fn ensure_must_run<CTX, K, V>(
tcx: CTX,
key: &K,
query: &QueryVtable<CTX, K, V>,
query: &QueryVTable<CTX, K, V>,
) -> (bool, Option<DepNode<CTX::DepKind>>)
where
K: crate::dep_graph::DepNodeParams<CTX::DepContext>,

View File

@ -1565,6 +1565,8 @@ symbols! {
volatile_store,
vreg,
vreg_low16,
vtable_align,
vtable_size,
warn,
wasm_abi,
wasm_import_module,

View File

@ -67,7 +67,7 @@ pub(super) fn mangle<'tcx>(
)
.unwrap();
if let ty::InstanceDef::VtableShim(..) = instance.def {
if let ty::InstanceDef::VTableShim(..) = instance.def {
let _ = printer.write_str("{{vtable-shim}}");
}
@ -129,7 +129,7 @@ fn get_symbol_hash<'tcx>(
}
// We want to avoid accidental collision between different types of instances.
// Especially, `VtableShim`s and `ReifyShim`s may overlap with their original
// Especially, `VTableShim`s and `ReifyShim`s may overlap with their original
// instances without this.
discriminant(&instance.def).hash_stable(hcx, &mut hasher);
});

View File

@ -42,7 +42,7 @@ pub(super) fn mangle<'tcx>(
// Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
let shim_kind = match instance.def {
ty::InstanceDef::VtableShim(_) => Some("vtable"),
ty::InstanceDef::VTableShim(_) => Some("vtable"),
ty::InstanceDef::ReifyShim(_) => Some("reify"),
_ => None,

View File

@ -69,7 +69,7 @@ enum PointerKind<'tcx> {
/// No metadata attached, ie pointer to sized type or foreign type
Thin,
/// A trait object
Vtable(Option<DefId>),
VTable(Option<DefId>),
/// Slice
Length,
/// The unsize info of this projection
@ -102,7 +102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(match *t.kind() {
ty::Slice(_) | ty::Str => Some(PointerKind::Length),
ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())),
ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())),
ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() {
None => Some(PointerKind::Thin),
Some(f) => {
@ -951,7 +951,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
match fcx.pointer_kind(m_cast.ty, self.span)? {
None => Err(CastError::UnknownCastPtrKind),
Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
Some(PointerKind::Vtable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))),
Some(
PointerKind::OfProjection(_)

View File

@ -400,6 +400,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
sym::vtable_size | sym::vtable_align => {
(0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize)
}
other => {
tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other });
return;

View File

@ -2291,6 +2291,16 @@ extern "rust-intrinsic" {
/// [`std::hint::black_box`]: crate::hint::black_box
#[rustc_const_unstable(feature = "const_black_box", issue = "none")]
pub fn black_box<T>(dummy: T) -> T;
/// `ptr` must point to a vtable.
/// The intrinsic will return the size stored in that vtable.
#[cfg(not(bootstrap))]
pub fn vtable_size(ptr: *const ()) -> usize;
/// `ptr` must point to a vtable.
/// The intrinsic will return the alignment stored in that vtable.
#[cfg(not(bootstrap))]
pub fn vtable_align(ptr: *const ()) -> usize;
}
// Some functions are defined here because they accidentally got made

View File

@ -180,10 +180,20 @@ pub struct DynMetadata<Dyn: ?Sized> {
phantom: crate::marker::PhantomData<Dyn>,
}
#[cfg(not(bootstrap))]
extern "C" {
/// Opaque type for accessing vtables.
///
/// Private implementation detail of `DynMetadata::size_of` etc.
/// There is conceptually not actually any Abstract Machine memory behind this pointer.
type VTable;
}
/// The common prefix of all vtables. It is followed by function pointers for trait methods.
///
/// Private implementation detail of `DynMetadata::size_of` etc.
#[repr(C)]
#[cfg(bootstrap)]
struct VTable {
drop_in_place: fn(*mut ()),
size_of: usize,
@ -194,13 +204,28 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
/// Returns the size of the type associated with this vtable.
#[inline]
pub fn size_of(self) -> usize {
self.vtable_ptr.size_of
// Note that "size stored in vtable" is *not* the same as "result of size_of_val_raw".
// Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
// `Send` part!
#[cfg(bootstrap)]
return self.vtable_ptr.size_of;
#[cfg(not(bootstrap))]
// SAFETY: DynMetadata always contains a valid vtable pointer
return unsafe {
crate::intrinsics::vtable_size(self.vtable_ptr as *const VTable as *const ())
};
}
/// Returns the alignment of the type associated with this vtable.
#[inline]
pub fn align_of(self) -> usize {
self.vtable_ptr.align_of
#[cfg(bootstrap)]
return self.vtable_ptr.align_of;
#[cfg(not(bootstrap))]
// SAFETY: DynMetadata always contains a valid vtable pointer
return unsafe {
crate::intrinsics::vtable_align(self.vtable_ptr as *const VTable as *const ())
};
}
/// Returns the size and alignment together as a `Layout`

View File

@ -1,54 +0,0 @@
// run-pass
#![allow(dead_code)]
#[repr(C)]
union Transmute<T: Copy, U: Copy> {
t: T,
u: U,
}
trait Bar {
fn bar(&self) -> u32;
}
struct Foo {
foo: u32,
bar: bool,
}
impl Bar for Foo {
fn bar(&self) -> u32 {
self.foo
}
}
impl Drop for Foo {
fn drop(&mut self) {
assert!(!self.bar);
self.bar = true;
println!("dropping Foo");
}
}
#[derive(Copy, Clone)]
struct Fat<'a>(&'a Foo, &'static VTable);
struct VTable {
drop: Option<for<'a> fn(&'a mut Foo)>,
size: usize,
align: usize,
bar: for<'a> fn(&'a Foo) -> u32,
}
const FOO: &dyn Bar = &Foo { foo: 128, bar: false };
const G: Fat = unsafe { Transmute { t: FOO }.u };
const F: Option<for<'a> fn(&'a mut Foo)> = G.1.drop;
const H: for<'a> fn(&'a Foo) -> u32 = G.1.bar;
fn main() {
let mut foo = Foo { foo: 99, bar: false };
(F.unwrap())(&mut foo);
std::mem::forget(foo); // already ran the drop impl
assert_eq!(H(&Foo { foo: 42, bar: false }), 42);
}

View File

@ -2,19 +2,19 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:19:14
|
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid vtable: alignment `1000` is not a power of 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:24:14
|
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid vtable: size is bigger than largest supported object
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:34:1
--> $DIR/ub-incorrect-vtable.rs:33:1
|
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid vtable: alignment `1000` is not a power of 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
@ -22,16 +22,38 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:39:1
--> $DIR/ub-incorrect-vtable.rs:38:1
|
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid vtable: size is bigger than largest supported object
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼
}
error: aborting due to 4 previous errors
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:44:1
|
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:91:1
|
LL | const G: Wide = unsafe { Transmute { t: FOO }.u };
| ^^^^^^^^^^^^^ constructing invalid value at .1: encountered a dangling reference (going beyond the bounds of its allocation)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼
}
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0080`.

View File

@ -2,19 +2,19 @@ error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:19:14
|
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid vtable: alignment `1000` is not a power of 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:24:14
|
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid vtable: size is bigger than largest supported object
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:34:1
--> $DIR/ub-incorrect-vtable.rs:33:1
|
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid vtable: alignment `1000` is not a power of 2
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
@ -22,16 +22,38 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:39:1
--> $DIR/ub-incorrect-vtable.rs:38:1
|
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid vtable: size is bigger than largest supported object
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼
}
error: aborting due to 4 previous errors
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:44:1
|
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-incorrect-vtable.rs:91:1
|
LL | const G: Wide = unsafe { Transmute { t: FOO }.u };
| ^^^^^^^^^^^^^ constructing invalid value at .1: encountered a dangling reference (going beyond the bounds of its allocation)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼
}
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0080`.

View File

@ -18,27 +18,79 @@ trait Trait {}
const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
//~^ ERROR evaluation of constant value failed
//~| invalid vtable: alignment `1000` is not a power of 2
//~| does not point to a vtable
const INVALID_VTABLE_SIZE: &dyn Trait =
unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
//~^ ERROR evaluation of constant value failed
//~| invalid vtable: size is bigger than largest supported object
//~| does not point to a vtable
#[repr(transparent)]
struct W<T>(T);
// The drop fn is checked before size/align are, so get ourselves a "sufficiently valid" drop fn
fn drop_me(_: *mut usize) {}
const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) };
//~^^ ERROR it is undefined behavior to use this value
//~| invalid vtable: alignment `1000` is not a power of 2
//~| expected a vtable pointer
const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };
//~^^ ERROR it is undefined behavior to use this value
//~| invalid vtable: size is bigger than largest supported object
//~| expected a vtable pointer
// Even if the vtable has a fn ptr and a reasonable size+align, it still does not work.
const INVALID_VTABLE_UB: W<&dyn Trait> =
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1usize))) };
//~^^ ERROR it is undefined behavior to use this value
//~| expected a vtable pointer
// Trying to access the data in a vtable does not work, either.
#[derive(Copy, Clone)]
struct Wide<'a>(&'a Foo, &'static VTable);
struct VTable {
drop: Option<for<'a> fn(&'a mut Foo)>,
size: usize,
align: usize,
bar: for<'a> fn(&'a Foo) -> u32,
}
trait Bar {
fn bar(&self) -> u32;
}
struct Foo {
foo: u32,
bar: bool,
}
impl Bar for Foo {
fn bar(&self) -> u32 {
self.foo
}
}
impl Drop for Foo {
fn drop(&mut self) {
assert!(!self.bar);
self.bar = true;
println!("dropping Foo");
}
}
#[repr(C)]
union Transmute<T: Copy, U: Copy> {
t: T,
u: U,
}
const FOO: &dyn Bar = &Foo { foo: 128, bar: false };
const G: Wide = unsafe { Transmute { t: FOO }.u };
//~^ ERROR it is undefined behavior to use this value
//~| encountered a dangling reference
// (it is dangling because vtables do not contain memory that can be dereferenced)
fn main() {}

View File

@ -125,7 +125,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:56:1
|
LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
@ -147,7 +147,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:60:1
|
LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x0000000d, but expected a function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
@ -158,7 +158,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:62:1
|
LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered pointer to alloc41, but expected a function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc41, but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {

View File

@ -125,7 +125,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:56:1
|
LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
@ -147,7 +147,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:60:1
|
LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x000000000000000d, but expected a function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
@ -158,7 +158,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:62:1
|
LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered pointer to alloc41, but expected a function pointer
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc41, but expected a function pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {

View File

@ -6,7 +6,7 @@ LL | const BAD_UPVAR: &dyn FnOnce() = &{
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─alloc3──╼ ╾─alloc6──╼ │ ╾──╼╾──╼
╾─alloc3──╼ ╾─alloc4──╼ │ ╾──╼╾──╼
}
error: aborting due to previous error

View File

@ -6,7 +6,7 @@ LL | const BAD_UPVAR: &dyn FnOnce() = &{
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼
╾───────alloc3────────╼ ╾───────alloc4────────╼ │ ╾──────╼╾──────╼
}
error: aborting due to previous error

View File

@ -209,7 +209,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:117:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
@ -217,10 +217,10 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:120:1
--> $DIR/ub-wide-ptr.rs:121:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
@ -228,54 +228,39 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:123:1
--> $DIR/ub-wide-ptr.rs:125:1
|
LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered dangling vtable pointer in wide pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0x4[noalloc], but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ 04 00 00 00 │ ╾──╼....
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:125:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:128:57
|
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered unaligned vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:127:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:131:57
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid drop function pointer in vtable (not pointing to a function)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:129:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:134:56
|
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid drop function pointer in vtable (not pointing to a function)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:131:1
--> $DIR/ub-wide-ptr.rs:137:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid drop function pointer in vtable (not pointing to a function)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
@ -283,7 +268,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:135:1
--> $DIR/ub-wide-ptr.rs:142:1
|
LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.<dyn-downcast>: encountered 0x03, but expected a boolean
@ -293,39 +278,29 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_,
╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:139:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:147:62
|
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered dangling vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ 00 00 00 00 │ ╾──╼....
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:141:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:150:65
|
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered too small vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:147:5
--> $DIR/ub-wide-ptr.rs:157:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:151:5
--> $DIR/ub-wide-ptr.rs:161:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 12 bytes starting at offset N is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error: aborting due to 32 previous errors

View File

@ -209,7 +209,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:117:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
@ -217,10 +217,10 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:120:1
--> $DIR/ub-wide-ptr.rs:121:1
|
LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered too small vtable
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
@ -228,54 +228,39 @@ LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:123:1
--> $DIR/ub-wide-ptr.rs:125:1
|
LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered dangling vtable pointer in wide pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0x4[noalloc], but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:125:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:128:57
|
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered unaligned vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:127:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:131:57
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid drop function pointer in vtable (not pointing to a function)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:129:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:134:56
|
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid drop function pointer in vtable (not pointing to a function)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:131:1
--> $DIR/ub-wide-ptr.rs:137:1
|
LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered invalid drop function pointer in vtable (not pointing to a function)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered allocN, but expected a vtable pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
@ -283,7 +268,7 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::trans
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:135:1
--> $DIR/ub-wide-ptr.rs:142:1
|
LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>.<dyn-downcast>: encountered 0x03, but expected a boolean
@ -293,39 +278,29 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_,
╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:139:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:147:62
|
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered dangling vtable pointer in wide pointer
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-wide-ptr.rs:141:1
error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:150:65
|
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered too small vtable
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:147:5
--> $DIR/ub-wide-ptr.rs:157:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
error[E0080]: could not evaluate static initializer
--> $DIR/ub-wide-ptr.rs:151:5
--> $DIR/ub-wide-ptr.rs:161:5
|
LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 24 bytes starting at offset N is out-of-bounds
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
error: aborting due to 32 previous errors

View File

@ -116,30 +116,40 @@ const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe {
// bad trait object
const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) };
//~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad trait object
const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) };
//~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad trait object
const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) };
//~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| does not point to a vtable
const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| does not point to a vtable
const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| does not point to a vtable
const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) };
//~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad data *inside* the trait object
const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
//~^ ERROR it is undefined behavior to use this value
//~| expected a boolean
// # raw trait object
const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| null pointer
const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
//~^ ERROR it is undefined behavior to use this value
//~^ ERROR evaluation of constant value failed
//~| does not point to a vtable
const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) } as *const dyn Trait; // ok because raw
// Const eval fails for these, so they need to be statics to error.

View File

@ -2,11 +2,11 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/issue-79690.rs:30:1
|
LL | const G: Fat = unsafe { Transmute { t: FOO }.u };
| ^^^^^^^^^^^^ constructing invalid value at .1.<deref>.size.foo: encountered (potentially part of) a pointer, but expected plain (non-pointer) bytes
| ^^^^^^^^^^^^ constructing invalid value at .1: encountered a dangling reference (going beyond the bounds of its allocation)
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼
╾───────alloc3────────╼ ╾───────alloc4────────╼ │ ╾──────╼╾──────╼
}
error: aborting due to previous error

View File

@ -17,7 +17,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 4) {
╾─alloc7──╼ ╾─alloc9──╼ │ ╾──╼╾──╼
╾─alloc7──╼ ╾─alloc8──╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value
@ -28,7 +28,7 @@ LL | const BLUNT: &mut i32 = &mut 42;
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 4, align: 4) {
╾─alloc11─╼ │ ╾──╼
╾─alloc10─╼ │ ╾──╼
}
warning: skipping const checks

View File

@ -17,7 +17,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) };
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc7────────╼ ╾───────alloc9────────╼ │ ╾──────╼╾──────╼
╾───────alloc7────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value
@ -28,7 +28,7 @@ LL | const BLUNT: &mut i32 = &mut 42;
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: 8, align: 8) {
╾───────alloc11───────╼ │ ╾──────╼
╾───────alloc10───────╼ │ ╾──────╼
}
warning: skipping const checks

View File

@ -34,6 +34,11 @@ fn foo(d: &dyn D) {
d.foo_d();
}
fn bar(d: &dyn C) {
d.foo_c();
}
fn main() {
foo(&S);
bar(&S);
}

View File

@ -12,6 +12,7 @@
#[rustc_dump_vtable]
trait A {
//~^ error vtable
fn foo_a(&self) {}
}
@ -23,6 +24,7 @@ trait B {
#[rustc_dump_vtable]
trait C: A + B {
//~^ error vtable
fn foo_c(&self) {}
}
@ -115,8 +117,27 @@ impl M for S {}
impl N for S {}
impl O for S {}
fn foo(_: &dyn O) {}
macro_rules! monomorphize_vtable {
($trait:ident) => {{
fn foo(_ : &dyn $trait) {}
foo(&S);
}}
}
fn main() {
foo(&S);
monomorphize_vtable!(O);
monomorphize_vtable!(A);
monomorphize_vtable!(B);
monomorphize_vtable!(C);
monomorphize_vtable!(D);
monomorphize_vtable!(E);
monomorphize_vtable!(F);
monomorphize_vtable!(H);
monomorphize_vtable!(I);
monomorphize_vtable!(J);
monomorphize_vtable!(K);
monomorphize_vtable!(L);
monomorphize_vtable!(M);
monomorphize_vtable!(N);
}

View File

@ -29,29 +29,54 @@ error: vtable entries for `<S as O>`: [
TraitVPtr(<S as N>),
Method(<S as O>::foo_o),
]
--> $DIR/vtable-multi-level.rs:95:1
--> $DIR/vtable-multi-level.rs:97:1
|
LL | trait O: G + N {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as A>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
]
--> $DIR/vtable-multi-level.rs:14:1
|
LL | trait A {
| ^^^^^^^
error: vtable entries for `<S as B>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as B>::foo_b),
]
--> $DIR/vtable-multi-level.rs:19:1
--> $DIR/vtable-multi-level.rs:20:1
|
LL | trait B {
| ^^^^^^^
error: vtable entries for `<S as C>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as A>::foo_a),
Method(<S as B>::foo_b),
TraitVPtr(<S as B>),
Method(<S as C>::foo_c),
]
--> $DIR/vtable-multi-level.rs:26:1
|
LL | trait C: A + B {
| ^^^^^^^^^^^^^^
error: vtable entries for `<S as D>`: [
MetadataDropInPlace,
MetadataSize,
MetadataAlign,
Method(<S as D>::foo_d),
]
--> $DIR/vtable-multi-level.rs:30:1
--> $DIR/vtable-multi-level.rs:32:1
|
LL | trait D {
| ^^^^^^^
@ -62,7 +87,7 @@ error: vtable entries for `<S as E>`: [
MetadataAlign,
Method(<S as E>::foo_e),
]
--> $DIR/vtable-multi-level.rs:36:1
--> $DIR/vtable-multi-level.rs:38:1
|
LL | trait E {
| ^^^^^^^
@ -76,7 +101,7 @@ error: vtable entries for `<S as F>`: [
TraitVPtr(<S as E>),
Method(<S as F>::foo_f),
]
--> $DIR/vtable-multi-level.rs:42:1
--> $DIR/vtable-multi-level.rs:44:1
|
LL | trait F: D + E {
| ^^^^^^^^^^^^^^
@ -87,7 +112,7 @@ error: vtable entries for `<S as H>`: [
MetadataAlign,
Method(<S as H>::foo_h),
]
--> $DIR/vtable-multi-level.rs:53:1
--> $DIR/vtable-multi-level.rs:55:1
|
LL | trait H {
| ^^^^^^^
@ -98,7 +123,7 @@ error: vtable entries for `<S as I>`: [
MetadataAlign,
Method(<S as I>::foo_i),
]
--> $DIR/vtable-multi-level.rs:59:1
--> $DIR/vtable-multi-level.rs:61:1
|
LL | trait I {
| ^^^^^^^
@ -112,7 +137,7 @@ error: vtable entries for `<S as J>`: [
TraitVPtr(<S as I>),
Method(<S as J>::foo_j),
]
--> $DIR/vtable-multi-level.rs:65:1
--> $DIR/vtable-multi-level.rs:67:1
|
LL | trait J: H + I {
| ^^^^^^^^^^^^^^
@ -123,7 +148,7 @@ error: vtable entries for `<S as K>`: [
MetadataAlign,
Method(<S as K>::foo_k),
]
--> $DIR/vtable-multi-level.rs:71:1
--> $DIR/vtable-multi-level.rs:73:1
|
LL | trait K {
| ^^^^^^^
@ -134,7 +159,7 @@ error: vtable entries for `<S as L>`: [
MetadataAlign,
Method(<S as L>::foo_l),
]
--> $DIR/vtable-multi-level.rs:77:1
--> $DIR/vtable-multi-level.rs:79:1
|
LL | trait L {
| ^^^^^^^
@ -148,7 +173,7 @@ error: vtable entries for `<S as M>`: [
TraitVPtr(<S as L>),
Method(<S as M>::foo_m),
]
--> $DIR/vtable-multi-level.rs:83:1
--> $DIR/vtable-multi-level.rs:85:1
|
LL | trait M: K + L {
| ^^^^^^^^^^^^^^
@ -169,10 +194,10 @@ error: vtable entries for `<S as N>`: [
TraitVPtr(<S as M>),
Method(<S as N>::foo_n),
]
--> $DIR/vtable-multi-level.rs:89:1
--> $DIR/vtable-multi-level.rs:91:1
|
LL | trait N: J + M {
| ^^^^^^^^^^^^^^
error: aborting due to 12 previous errors
error: aborting due to 14 previous errors

View File

@ -25,7 +25,9 @@ impl B for S {}
impl C for S {}
fn foo(c: &dyn C) {}
fn bar(c: &dyn B) {}
fn main() {
foo(&S);
bar(&S);
}