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) => { Scalar::Ptr(ptr, _size) => {
let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative 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 fx.tcx.global_alloc(alloc_id) {
let base_addr = match alloc_kind { GlobalAlloc::Memory(alloc) => {
Some(GlobalAlloc::Memory(alloc)) => {
let data_id = data_id_for_alloc_id( let data_id = data_id_for_alloc_id(
&mut fx.constants_cx, &mut fx.constants_cx,
fx.module, fx.module,
@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>(
} }
fx.bcx.ins().global_value(fx.pointer_type, local_data_id) 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 func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
let local_func_id = let local_func_id =
fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) 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)); assert!(fx.tcx.is_static(def_id));
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
let local_data_id = 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) fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
} }
None => bug!("missing allocation {:?}", alloc_id),
}; };
let val = if offset.bytes() != 0 { let val = if offset.bytes() != 0 {
fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap()) 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() { while let Some(todo_item) = cx.todo.pop() {
let (data_id, alloc, section_name) = match todo_item { let (data_id, alloc, section_name) = match todo_item {
TodoItem::Alloc(alloc_id) => { TodoItem::Alloc(alloc_id) => {
//println!("alloc_id {}", alloc_id); let alloc = match tcx.global_alloc(alloc_id) {
let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
GlobalAlloc::Memory(alloc) => alloc, 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(|| { let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
module module
@ -424,7 +437,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
read_target_uint(endianness, bytes).unwrap() 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 { let data_id = match reloc_target_alloc {
GlobalAlloc::Function(instance) => { GlobalAlloc::Function(instance) => {
assert_eq!(addend, 0); 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) => { GlobalAlloc::Memory(target_alloc) => {
data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) 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) => { GlobalAlloc::Static(def_id) => {
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) 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)); 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_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem
| unchecked_shl | unchecked_shr, (c x, c y) { | unchecked_shl | unchecked_shr, (c x, c y) {
// FIXME trap on overflow // FIXME trap on overflow

View File

@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
GlobalAlloc::Function(fn_instance) => { GlobalAlloc::Function(fn_instance) => {
self.get_fn_addr(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) => { GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id)); assert!(self.tcx.is_static(def_id));
self.get_static(def_id).get_address(None) 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.get_fn_addr(fn_instance.polymorphize(self.tcx)),
self.data_layout().instruction_address_space, 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) => { GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id)); assert!(self.tcx.is_static(def_id));
assert!(!self.tcx.is_thread_local_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) { let address_space = match cx.tcx.global_alloc(alloc_id) {
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space, 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( llvals.push(cx.scalar_to_backend(

View File

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

View File

@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> {
pub enum Stub<'ll> { pub enum Stub<'ll> {
Struct, Struct,
Union, Union,
VtableTy { vtable_holder: &'ll DIType }, VTableTy { vtable_holder: &'ll DIType },
} }
pub struct StubInfo<'ll, 'tcx> { 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 unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
let metadata = match kind { let metadata = match kind {
Stub::Struct | Stub::VtableTy { .. } => { Stub::Struct | Stub::VTableTy { .. } => {
let vtable_holder = match kind { let vtable_holder = match kind {
Stub::VtableTy { vtable_holder } => Some(vtable_holder), Stub::VTableTy { vtable_holder } => Some(vtable_holder),
_ => None, _ => None,
}; };
unsafe { 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); let new_vptr = bx.load(ptr_ty, gep, ptr_align);
bx.nonnull_metadata(new_vptr); bx.nonnull_metadata(new_vptr);
// Vtable loads are invariant. // VTable loads are invariant.
bx.set_invariant_load(new_vptr); bx.set_invariant_load(new_vptr);
new_vptr new_vptr
} else { } else {

View File

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

View File

@ -3,12 +3,16 @@ use super::place::PlaceRef;
use super::FunctionCx; use super::FunctionCx;
use crate::common::{span_invalid_monomorphization_error, IntPredicate}; use crate::common::{span_invalid_monomorphization_error, IntPredicate};
use crate::glue; use crate::glue;
use crate::meth;
use crate::traits::*; use crate::traits::*;
use crate::MemFlags; use crate::MemFlags;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{sym, Span}; 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>>( fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx, 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()) 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::pref_align_of
| sym::needs_drop | sym::needs_drop
| sym::type_id | sym::type_id

View File

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

View File

@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
let mplace = ecx.deref_operand(&op).unwrap(); let mplace = ecx.deref_operand(&op).unwrap();
if let Some(alloc_id) = mplace.ptr.provenance { if let Some(alloc_id) = mplace.ptr.provenance {
assert_eq!( 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, Mutability::Not,
"deref_mir_constant cannot be used with mutable allocations as \ "deref_mir_constant cannot be used with mutable allocations as \
that could allow pattern matching to observe mutable statics", 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) self.write_immediate(val, dest)
} }
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
let val = self.read_immediate(src)?; let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?;
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 old_vptr = self.scalar_to_ptr(old_vptr)?;
let new_vptr = self let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
.read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?; if old_trait != data_a.principal() {
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) throw_ub_format!("upcast on a pointer whose vtable does not match its type");
} else {
self.write_immediate(*val, dest)
} }
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, _)) => { (_, &ty::Dynamic(ref data, _)) => {
// Initial cast from sized to dyn trait // 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 ptr = self.read_immediate(src)?.to_scalar()?;
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
self.write_immediate(val, dest) 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(..) => { ty::Dynamic(..) => {
let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?; let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?;
// Read size and align from vtable (already checks size). // 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 => { 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 // to validation to error -- it has the much better error messages, pointing out where
// in the value the dangling reference lies. // in the value the dangling reference lies.
// The `delay_span_bug` ensures that we don't forget such a check in validation. // 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"); tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
} }
// treat dangling pointers like other statics // treat dangling pointers like other statics
@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive<
.sess .sess
.span_err(ecx.tcx.span, "encountered dangling pointer in final constant"); .span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
return Err(reported); 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 // 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. // marked as dangling by local memory. That should be impossible.
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); 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])?; let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
self.write_scalar(result, dest)?; 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), _ => return Ok(false),
} }

View File

@ -16,7 +16,7 @@ use std::ptr;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::mir::display_allocation; 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 rustc_target::abi::{Align, HasDataLayout, Size};
use super::{ use super::{
@ -62,6 +62,8 @@ pub enum AllocKind {
LiveData, LiveData,
/// A function allocation (that fn ptrs point to). /// A function allocation (that fn ptrs point to).
Function, Function,
/// A (symbolic) vtable allocation.
VTable,
/// A dead allocation. /// A dead allocation.
Dead, Dead,
} }
@ -159,7 +161,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<M::Provenance>> { ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let alloc_id = ptr.provenance; let alloc_id = ptr.provenance;
// We need to handle `extern static`. // 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) => { Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
bug!("global memory cannot point to thread-local static") 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 { let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
// Deallocating global memory -- always an error // 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(..)) => { Some(GlobalAlloc::Function(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is a 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(..)) => { Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
err_ub_format!("deallocating {alloc_id:?}, which is static 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, id: AllocId,
is_write: bool, is_write: bool,
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> { ) -> 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)) => { Some(GlobalAlloc::Memory(mem)) => {
// Memory of a constant or promoted or anonymous memory referenced by a static. // Memory of a constant or promoted or anonymous memory referenced by a static.
(mem, None) (mem, None)
} }
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
None => throw_ub!(PointerUseAfterFree(id)), None => throw_ub!(PointerUseAfterFree(id)),
Some(GlobalAlloc::Static(def_id)) => { Some(GlobalAlloc::Static(def_id)) => {
assert!(self.tcx.is_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 // 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. // to another static), those inner references only exist in "resolved" form.
if self.tcx.is_foreign_item(def_id) { 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)); throw_unsup!(ReadExternStatic(def_id));
} }
@ -663,12 +671,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// # Statics // # Statics
// Can't do this in the match argument, we may get cycle errors since the lock would // Can't do this in the match argument, we may get cycle errors since the lock would
// be held throughout the match. // be held throughout the match.
match self.tcx.get_global_alloc(id) { match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Static(did)) => { Some(GlobalAlloc::Static(def_id)) => {
assert!(!self.tcx.is_thread_local_static(did)); assert!(self.tcx.is_static(def_id));
assert!(!self.tcx.is_thread_local_static(def_id));
// Use size and align of the type. // 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(); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
assert!(!layout.is_unsized());
(layout.size, layout.align.abi, AllocKind::LiveData) (layout.size, layout.align.abi, AllocKind::LiveData)
} }
Some(GlobalAlloc::Memory(alloc)) => { 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) (alloc.size(), alloc.align, AllocKind::LiveData)
} }
Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"), 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. // The rest must be dead.
None => { None => {
// Deallocated pointers are allowed, we should be able to find // 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) { if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) {
Some(FnVal::Other(*extra)) Some(FnVal::Other(*extra))
} else { } else {
match self.tcx.get_global_alloc(id) { match self.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)), Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)),
_ => None, _ => None,
} }
@ -716,7 +730,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&self, &self,
ptr: Pointer<Option<M::Provenance>>, ptr: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { ) -> 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)?; let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 { if offset.bytes() != 0 {
throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) 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()) .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> { pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not; self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not;
Ok(()) Ok(())
@ -829,7 +858,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
} }
None => { None => {
// global alloc // global alloc
match self.ecx.tcx.get_global_alloc(id) { match self.ecx.tcx.try_get_global_alloc(id) {
Some(GlobalAlloc::Memory(alloc)) => { Some(GlobalAlloc::Memory(alloc)) => {
write!(fmt, " (unchanged global, ")?; write!(fmt, " (unchanged global, ")?;
write_allocation_track_relocs( 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)) => { 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)) => { Some(GlobalAlloc::Static(did)) => {
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(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. /// 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( pub(super) fn unpack_dyn_trait(
&self, &self,
mplace: &MPlaceTy<'tcx, M::Provenance>, 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 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)?; 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 { let mplace = MPlaceTy {
mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace }, mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace },
layout, layout,
align: layout.align.abi, align: layout.align.abi,
}; };
Ok((instance, mplace)) Ok(mplace)
} }
} }

View File

@ -1,5 +1,4 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryFrom;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::Instance; 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. // caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
M::call_intrinsic(self, instance, args, destination, target, unwind) M::call_intrinsic(self, instance, args, destination, target, unwind)
} }
ty::InstanceDef::VtableShim(..) ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::FnPtrShim(..) | 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 // 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(); let mut args = args.to_vec();
// We have to implement all "object safe receivers". So we have to go search for a // 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 // 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>, // Obtain the underlying trait we are working on.
// i.e., it is still not necessarily `ty::Dynamic` (so we cannot use let receiver_tail = self
// `place.vtable()`), but it should have a `dyn Trait` tail. .tcx
assert!(matches!( .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
self.tcx let ty::Dynamic(data, ..) = receiver_tail.kind() else {
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env) span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
.kind(), };
ty::Dynamic(..)
)); // Get the required information from the vtable.
let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?; let vptr = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?; 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 // `*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 // 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]); trace!("Patched receiver operand to {:#?}", args[0]);
// recurse with concrete function // recurse with concrete function
self.eval_fn_call( self.eval_fn_call(
fn_val, FnVal::Instance(fn_inst),
(caller_abi, caller_fn_abi), (caller_abi, caller_fn_abi),
&args, &args,
with_caller_location, 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() { let (instance, place) = match place.layout.ty.kind() {
ty::Dynamic(..) => { ty::Dynamic(..) => {
// Dropping a trait object. // Dropping a trait object. Need to find actual drop fn.
self.unpack_dyn_trait(&place)? let place = self.unpack_dyn_trait(&place)?;
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
(instance, place)
} }
_ => (instance, place), _ => (instance, place),
}; };

View File

@ -1,24 +1,21 @@
use std::convert::TryFrom; use rustc_middle::mir::interpret::{InterpResult, Pointer};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::mir::interpret::{alloc_range, InterpResult, Pointer, PointerArithmetic}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{
self, Ty, TyCtxt, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE,
COMMON_VTABLE_ENTRIES_SIZE,
};
use rustc_target::abi::{Align, Size}; use rustc_target::abi::{Align, Size};
use super::util::ensure_monomorphic_enough; 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> { 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 /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
/// objects. /// objects.
/// ///
/// The `trait_ref` encodes the erased self type. Hence, if we are /// The `trait_ref` encodes the erased self type. Hence, if we are making an object `Foo<Trait>`
/// making an object `Foo<Trait>` from a value of type `Foo<T>`, then /// from a value of type `Foo<T>`, then `trait_ref` would map `T: Trait`. `None` here means that
/// `trait_ref` would map `T: Trait`. /// this is an auto trait without any methods, so we only need the basic vtable (drop, size,
pub fn get_vtable( /// align).
&mut self, pub fn get_vtable_ptr(
&self,
ty: Ty<'tcx>, ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { ) -> 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, ty)?;
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref)); 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))?;
let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?;
Ok(vtable_ptr.into()) Ok(vtable_ptr.into())
} }
/// Resolves the function at the specified slot in the provided /// Returns a high-level representation of the entires of the given vtable.
/// vtable. Currently an index of '3' (`TyCtxt::COMMON_VTABLE_ENTRIES.len()`) pub fn get_vtable_entries(
/// corresponds to the first method declared in the trait of the provided vtable.
pub fn get_vtable_slot(
&self, &self,
vtable: Pointer<Option<M::Provenance>>, vtable: Pointer<Option<M::Provenance>>,
idx: u64, ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> {
) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?;
let ptr_size = self.pointer_size(); Ok(if let Some(poly_trait_ref) = poly_trait_ref {
let vtable_slot = vtable.offset(ptr_size * idx, self)?; let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty);
let vtable_slot = self let trait_ref = self.tcx.erase_regions(trait_ref);
.get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? self.tcx.vtable_entries(trait_ref)
.expect("cannot be a ZST"); } else {
let fn_ptr = self.scalar_to_ptr(vtable_slot.read_pointer(Size::ZERO)?.check_init()?)?; TyCtxt::COMMON_VTABLE_ENTRIES
self.get_ptr_fn(fn_ptr) })
} }
/// Returns the drop fn instance as well as the actual dynamic type. pub fn get_vtable_size_and_align(
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(
&self, &self,
vtable: Pointer<Option<M::Provenance>>, vtable: Pointer<Option<M::Provenance>>,
) -> InterpResult<'tcx, (Size, Align)> { ) -> InterpResult<'tcx, (Size, Align)> {
let pointer_size = self.pointer_size(); let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?;
// We check for `size = 3 * ptr_size`, which covers the drop fn (unused here), let layout = self.layout_of(ty)?;
// the size, and the align (which we read below). assert!(!layout.is_unsized(), "there are no vtables for unsized types");
let vtable = self Ok((layout.size, layout.align.abi))
.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)
} }
} }

View File

@ -313,50 +313,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
match tail.kind() { match tail.kind() {
ty::Dynamic(..) => { ty::Dynamic(..) => {
let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?; let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta())?;
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines. // Make sure it is a genuine vtable pointer.
try_validation!( let (_ty, _trait) = try_validation!(
self.ecx.check_ptr_access_align( self.ecx.get_ptr_vtable(vtable),
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
),
self.path, self.path,
err_ub!(DanglingIntPointer(..)) | err_ub!(DanglingIntPointer(..)) |
err_ub!(PointerUseAfterFree(..)) => err_ub!(InvalidVTablePointer(..)) =>
{ "dangling vtable pointer in wide pointer" }, { "{vtable}" } expected { "a vtable pointer" },
err_ub!(AlignmentCheckFailed { .. }) =>
{ "unaligned vtable pointer in wide pointer" },
err_ub!(PointerOutOfBounds { .. }) =>
{ "too small vtable" },
); );
try_validation!( // FIXME: check if the type/trait match what ty::Dynamic says?
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.
} }
ty::Slice(..) | ty::Str => { ty::Slice(..) | ty::Str => {
let _len = try_validation!( 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. // `!` 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) { 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). // 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 { if let Some(GlobalAlloc::Static(did)) = alloc_kind {
assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(!self.ecx.tcx.is_thread_local_static(did));
assert!(self.ecx.tcx.is_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!( let _fn = try_validation!(
self.ecx.get_ptr_fn(ptr), self.ecx.get_ptr_fn(ptr),
self.path, self.path,
err_ub!(DanglingIntPointer(0, _)) =>
{ "a null function pointer" },
err_ub!(DanglingIntPointer(..)) | err_ub!(DanglingIntPointer(..)) |
err_ub!(InvalidFunctionPointer(..)) => err_ub!(InvalidFunctionPointer(..)) =>
{ "{:x}", value } expected { "a function pointer" }, { "{ptr}" } expected { "a function pointer" },
); );
// FIXME: Check if the signature matches // FIXME: Check if the signature matches
} else { } else {

View File

@ -425,7 +425,7 @@ macro_rules! make_value_visitor {
// unsized values are never immediate, so we can assert_mem_place // unsized values are never immediate, so we can assert_mem_place
let op = v.to_op_for_read(self.ecx())?; let op = v.to_op_for_read(self.ecx())?;
let dest = op.assert_mem_place(); 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); trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type // recurse with the inner type
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); 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 super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
use crate::mir::interpret::ConstValue; 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_data_structures::sync::Lock;
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; 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. /// 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! /// Free-form case. Only for errors that are never caught!
Ub(String), Ub(String),
/// Unreachable code was executed. /// Unreachable code was executed.
@ -241,12 +241,6 @@ pub enum UndefinedBehaviorInfo<'tcx> {
PointerArithOverflow, PointerArithOverflow,
/// Invalid metadata in a wide pointer (using `str` to avoid allocations). /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
InvalidMeta(&'static str), 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. /// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer), UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed. /// Dereferencing a dangling pointer after it got freed.
@ -271,6 +265,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
WriteToReadOnly(AllocId), WriteToReadOnly(AllocId),
// Trying to access the data behind a function pointer. // Trying to access the data behind a function pointer.
DerefFunctionPointer(AllocId), DerefFunctionPointer(AllocId),
// Trying to access the data behind a vtable pointer.
DerefVTablePointer(AllocId),
/// The value validity check found a problem. /// The value validity check found a problem.
/// Should only be thrown by `validity.rs` and always point out which part of the value /// Should only be thrown by `validity.rs` and always point out which part of the value
/// is the problem. /// is the problem.
@ -288,6 +284,8 @@ pub enum UndefinedBehaviorInfo<'tcx> {
InvalidTag(Scalar), InvalidTag(Scalar),
/// Using a pointer-not-to-a-function as function pointer. /// Using a pointer-not-to-a-function as function pointer.
InvalidFunctionPointer(Pointer), InvalidFunctionPointer(Pointer),
/// Using a pointer-not-to-a-vtable as vtable pointer.
InvalidVTablePointer(Pointer),
/// Using a string that is not valid UTF-8, /// Using a string that is not valid UTF-8,
InvalidStr(std::str::Utf8Error), InvalidStr(std::str::Utf8Error),
/// Using uninitialized data where it is not allowed. /// Using uninitialized data where it is not allowed.
@ -300,7 +298,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
UninhabitedEnumVariantWritten, UninhabitedEnumVariantWritten,
} }
impl fmt::Display for UndefinedBehaviorInfo<'_> { impl fmt::Display for UndefinedBehaviorInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use UndefinedBehaviorInfo::*; use UndefinedBehaviorInfo::*;
match self { match self {
@ -315,14 +313,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"), 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!( UnterminatedCString(p) => write!(
f, f,
"reading a null-terminated string starting at {p:?} with no null found before end of allocation", "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"), WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"), DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
ValidationFailure { path: None, msg } => { ValidationFailure { path: None, msg } => {
write!(f, "constructing invalid value: {msg}") write!(f, "constructing invalid value: {msg}")
} }
@ -375,6 +366,9 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
InvalidFunctionPointer(p) => { InvalidFunctionPointer(p) => {
write!(f, "using {p:?} as function pointer but it does not point to a function") 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}"), InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
InvalidUninitBytes(Some((alloc, info))) => write!( InvalidUninitBytes(Some((alloc, info))) => write!(
f, f,
@ -494,7 +488,7 @@ impl dyn MachineStopType {
pub enum InterpError<'tcx> { pub enum InterpError<'tcx> {
/// The program caused undefined behavior. /// 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 /// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure). /// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo), Unsupported(UnsupportedOpInfo),

View File

@ -196,6 +196,7 @@ impl fmt::Debug for AllocId {
enum AllocDiscriminant { enum AllocDiscriminant {
Alloc, Alloc,
Fn, Fn,
VTable,
Static, Static,
} }
@ -215,6 +216,12 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
AllocDiscriminant::Fn.encode(encoder); AllocDiscriminant::Fn.encode(encoder);
fn_instance.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) => { GlobalAlloc::Static(did) => {
assert!(!tcx.is_thread_local_static(did)); assert!(!tcx.is_thread_local_static(did));
// References to statics doesn't need to know about their allocations, // 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); State::InProgress(TinyList::new_single(self.session_id), alloc_id);
Some(alloc_id) Some(alloc_id)
} }
AllocDiscriminant::Fn | AllocDiscriminant::Static => { AllocDiscriminant::Fn
| AllocDiscriminant::Static
| AllocDiscriminant::VTable => {
// Fns and statics cannot be cyclic, and their `AllocId` // Fns and statics cannot be cyclic, and their `AllocId`
// is determined later by interning. // is determined later by interning.
*entry = *entry =
@ -355,6 +364,16 @@ impl<'s> AllocDecodingSession<'s> {
let alloc_id = decoder.interner().create_fn_alloc(instance); let alloc_id = decoder.interner().create_fn_alloc(instance);
alloc_id 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 => { AllocDiscriminant::Static => {
assert!(alloc_id.is_none()); assert!(alloc_id.is_none());
trace!("creating extern static alloc ID"); trace!("creating extern static alloc ID");
@ -380,6 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
pub enum GlobalAlloc<'tcx> { pub enum GlobalAlloc<'tcx> {
/// The alloc ID is used as a function pointer. /// The alloc ID is used as a function pointer.
Function(Instance<'tcx>), 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). /// 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. /// This is also used to break the cycle in recursive statics.
Static(DefId), Static(DefId),
@ -407,6 +428,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
_ => bug!("expected function, got {:?}", self), _ => 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> { 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. /// 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 /// Should only be used for "symbolic" allocations (function pointers, vtables, statics), we
/// to dedup IDs for "real" memory! /// don't want to dedup IDs for "real" memory!
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId { fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
let mut alloc_map = self.alloc_map.lock(); let mut alloc_map = self.alloc_map.lock();
match alloc { match alloc {
GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {}
GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"),
} }
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { 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 /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
/// `Allocation` with a different `AllocId`. /// `Allocation` with a different `AllocId`.
/// Statics with identical content will still point to the same `Allocation`, i.e., /// 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- /// This function exists to allow const eval to detect the difference between evaluation-
/// local dangling pointers and allocations in constants/statics. /// local dangling pointers and allocations in constants/statics.
#[inline] #[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() 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 /// ids), this function is frequently used throughout rustc, but should not be used within
/// the miri engine. /// the miri engine.
pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { 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, Some(alloc) => alloc,
None => bug!("could not find allocation for {id:?}"), 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 // the codegen tests and can even make item order
// unstable. // unstable.
InstanceDef::Item(def) => def.did.as_local().map(Idx::index), InstanceDef::Item(def) => def.did.as_local().map(Idx::index),
InstanceDef::VtableShim(..) InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..) | InstanceDef::ReifyShim(..)
| InstanceDef::Intrinsic(..) | InstanceDef::Intrinsic(..)
| InstanceDef::FnPtrShim(..) | InstanceDef::FnPtrShim(..)

View File

@ -720,11 +720,17 @@ pub fn write_allocations<'tcx>(
write!(w, "{}", display_allocation(tcx, alloc.inner())) write!(w, "{}", display_allocation(tcx, alloc.inner()))
}; };
write!(w, "\n{id:?}")?; 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 // 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. // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
None => write!(w, " (deallocated)")?, None => write!(w, " (deallocated)")?,
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?, 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) => { Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
match tcx.eval_static_initializer(did) { match tcx.eval_static_initializer(did) {
Ok(alloc) => { Ok(alloc) => {

View File

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

View File

@ -523,4 +523,5 @@ impl_binder_encode_decode! {
ty::ExistentialPredicate<'tcx>, ty::ExistentialPredicate<'tcx>,
ty::TraitRef<'tcx>, ty::TraitRef<'tcx>,
Vec<ty::GeneratorInteriorTypeCause<'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| { ty::tls::with_opt(|tcx| {
trace!("hashing {:?}", *self); trace!("hashing {:?}", *self);
let tcx = tcx.expect("can't hash AllocIds during hir lowering"); 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` - /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` -
/// and dereference the argument to call the original function. /// 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. /// `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 { pub fn def_id(self) -> DefId {
match self { match self {
InstanceDef::Item(def) => def.did, InstanceDef::Item(def) => def.did,
InstanceDef::VtableShim(def_id) InstanceDef::VTableShim(def_id)
| InstanceDef::ReifyShim(def_id) | InstanceDef::ReifyShim(def_id)
| InstanceDef::FnPtrShim(def_id, _) | InstanceDef::FnPtrShim(def_id, _)
| InstanceDef::Virtual(def_id, _) | InstanceDef::Virtual(def_id, _)
@ -161,7 +161,7 @@ impl<'tcx> InstanceDef<'tcx> {
match self { match self {
ty::InstanceDef::Item(def) => Some(def.did), ty::InstanceDef::Item(def) => Some(def.did),
ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id), ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id),
InstanceDef::VtableShim(..) InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..) | InstanceDef::ReifyShim(..)
| InstanceDef::FnPtrShim(..) | InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..) | InstanceDef::Virtual(..)
@ -176,7 +176,7 @@ impl<'tcx> InstanceDef<'tcx> {
pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> { pub fn with_opt_param(self) -> ty::WithOptConstParam<DefId> {
match self { match self {
InstanceDef::Item(def) => def, InstanceDef::Item(def) => def,
InstanceDef::VtableShim(def_id) InstanceDef::VTableShim(def_id)
| InstanceDef::ReifyShim(def_id) | InstanceDef::ReifyShim(def_id)
| InstanceDef::FnPtrShim(def_id, _) | InstanceDef::FnPtrShim(def_id, _)
| InstanceDef::Virtual(def_id, _) | InstanceDef::Virtual(def_id, _)
@ -273,7 +273,7 @@ impl<'tcx> InstanceDef<'tcx> {
| InstanceDef::Intrinsic(..) | InstanceDef::Intrinsic(..)
| InstanceDef::ReifyShim(..) | InstanceDef::ReifyShim(..)
| InstanceDef::Virtual(..) | InstanceDef::Virtual(..)
| InstanceDef::VtableShim(..) => true, | InstanceDef::VTableShim(..) => true,
} }
} }
} }
@ -290,7 +290,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> {
match self.def { match self.def {
InstanceDef::Item(_) => Ok(()), InstanceDef::Item(_) => Ok(()),
InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
@ -434,7 +434,7 @@ impl<'tcx> Instance<'tcx> {
&& tcx.generics_of(def_id).has_self; && tcx.generics_of(def_id).has_self;
if is_vtable_shim { if is_vtable_shim {
debug!(" => associated item with unsizeable self: Self"); debug!(" => associated item with unsizeable self: Self");
Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) Some(Instance { def: InstanceDef::VTableShim(def_id), substs })
} else { } else {
Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| {
match resolved.def { match resolved.def {

View File

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

View File

@ -1269,7 +1269,7 @@ pub trait PrettyPrinter<'tcx>:
if let ty::Array(elem, len) = inner.kind() { if let ty::Array(elem, len) = inner.kind() {
if let ty::Uint(ty::UintTy::U8) = elem.kind() { if let ty::Uint(ty::UintTy::U8) = elem.kind() {
if let ty::ConstKind::Value(ty::ValTree::Leaf(int)) = len.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)) => { Some(GlobalAlloc::Memory(alloc)) => {
let len = int.assert_bits(self.tcx().data_layout.pointer_size); let len = int.assert_bits(self.tcx().data_layout.pointer_size);
let range = let range =
@ -1282,11 +1282,12 @@ pub trait PrettyPrinter<'tcx>:
p!("<too short allocation>") 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)) => { Some(GlobalAlloc::Static(def_id)) => {
p!(write("<static({:?})>", def_id)) p!(write("<static({:?})>", def_id))
} }
Some(GlobalAlloc::Function(_)) => p!("<function>"), Some(GlobalAlloc::Function(_)) => p!("<function>"),
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
None => p!("<dangling pointer>"), None => p!("<dangling pointer>"),
} }
return Ok(self); return Ok(self);
@ -1297,7 +1298,8 @@ pub trait PrettyPrinter<'tcx>:
ty::FnPtr(_) => { ty::FnPtr(_) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings" // 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). // 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( self = self.typed_value(
|this| this.print_value_path(instance.def_id(), instance.substs), |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> { fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
match self { match self {
ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), 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::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)),
ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)),
ty::InstanceDef::FnPtrShim(def_id, ty) => { 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)?, substs: self.substs.try_fold_with(folder)?,
def: match self.def { def: match self.def {
Item(def) => Item(def.try_fold_with(folder)?), 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)?), ReifyShim(did) => ReifyShim(did.try_fold_with(folder)?),
Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?), Intrinsic(did) => Intrinsic(did.try_fold_with(folder)?),
FnPtrShim(did, ty) => { FnPtrShim(did, ty) => {
@ -954,7 +954,7 @@ impl<'tcx> TypeVisitable<'tcx> for ty::instance::Instance<'tcx> {
self.substs.visit_with(visitor)?; self.substs.visit_with(visitor)?;
match self.def { match self.def {
Item(def) => def.visit_with(visitor), 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) did.visit_with(visitor)
} }
FnPtrShim(did, ty) | CloneShim(did, ty) => { 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 // 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 // do not need to catch this here, we can wait until the inliner decides to continue
// inlining a second time. // inlining a second time.
InstanceDef::VtableShim(_) InstanceDef::VTableShim(_)
| InstanceDef::ReifyShim(_) | InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..) | InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. } | 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 // 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 // again, a function item can end up getting inlined. Thus we'll be able to cause
// a cycle that way // a cycle that way
InstanceDef::VtableShim(_) InstanceDef::VTableShim(_)
| InstanceDef::ReifyShim(_) | InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..) | InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. } | 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 { let mut result = match instance {
ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", 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)) build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
} }
ty::InstanceDef::FnPtrShim(def_id, ty) => { ty::InstanceDef::FnPtrShim(def_id, ty) => {
@ -113,7 +113,7 @@ enum Adjustment {
/// We get passed `&[mut] self` and call the target with `*self`. /// 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 /// 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, Deref,
/// We get passed `self: Self` and call the target with `&mut self`. /// 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 // FIXME(eddyb) avoid having this snippet both here and in
// `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?). // `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, ...) // Modify fn(self, ...) to fn(self: *mut Self, ...)
let mut inputs_and_output = sig.inputs_and_output.to_vec(); let mut inputs_and_output = sig.inputs_and_output.to_vec();
let self_arg = &mut inputs_and_output[0]; let self_arg = &mut inputs_and_output[0];

View File

@ -25,7 +25,7 @@
//! codegen unit: //! codegen unit:
//! //!
//! - Constants //! - Constants
//! - Vtables //! - VTables
//! - Object Shims //! - Object Shims
//! //!
//! //!
@ -992,7 +992,7 @@ fn visit_instance_use<'tcx>(
} }
} }
ty::InstanceDef::DropGlue(_, Some(_)) ty::InstanceDef::DropGlue(_, Some(_))
| ty::InstanceDef::VtableShim(..) | ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::Item(..) | 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)); 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) => { MonoItem::Fn(instance) => {
let def_id = match instance.def { let def_id = match instance.def {
ty::InstanceDef::Item(def) => def.did, ty::InstanceDef::Item(def) => def.did,
ty::InstanceDef::VtableShim(..) ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::ClosureOnceShim { .. }
@ -425,7 +425,7 @@ fn mono_item_visibility<'tcx>(
InstanceDef::DropGlue(def_id, Some(_)) => def_id, InstanceDef::DropGlue(def_id, Some(_)) => def_id,
// These are all compiler glue and such, never exported, always hidden. // These are all compiler glue and such, never exported, always hidden.
InstanceDef::VtableShim(..) InstanceDef::VTableShim(..)
| InstanceDef::ReifyShim(..) | InstanceDef::ReifyShim(..)
| InstanceDef::FnPtrShim(..) | InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..) | InstanceDef::Virtual(..)

View File

@ -37,7 +37,7 @@ mod values;
use self::values::Value; use self::values::Value;
pub use rustc_query_system::query::QueryConfig; 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; mod on_disk_cache;
pub use on_disk_cache::OnDiskCache; pub use on_disk_cache::OnDiskCache;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -67,7 +67,7 @@ pub(super) fn mangle<'tcx>(
) )
.unwrap(); .unwrap();
if let ty::InstanceDef::VtableShim(..) = instance.def { if let ty::InstanceDef::VTableShim(..) = instance.def {
let _ = printer.write_str("{{vtable-shim}}"); 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. // 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. // instances without this.
discriminant(&instance.def).hash_stable(hcx, &mut hasher); 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. // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
let shim_kind = match instance.def { let shim_kind = match instance.def {
ty::InstanceDef::VtableShim(_) => Some("vtable"), ty::InstanceDef::VTableShim(_) => Some("vtable"),
ty::InstanceDef::ReifyShim(_) => Some("reify"), ty::InstanceDef::ReifyShim(_) => Some("reify"),
_ => None, _ => None,

View File

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

View File

@ -2291,6 +2291,16 @@ extern "rust-intrinsic" {
/// [`std::hint::black_box`]: crate::hint::black_box /// [`std::hint::black_box`]: crate::hint::black_box
#[rustc_const_unstable(feature = "const_black_box", issue = "none")] #[rustc_const_unstable(feature = "const_black_box", issue = "none")]
pub fn black_box<T>(dummy: T) -> T; 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 // 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>, 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. /// The common prefix of all vtables. It is followed by function pointers for trait methods.
/// ///
/// Private implementation detail of `DynMetadata::size_of` etc. /// Private implementation detail of `DynMetadata::size_of` etc.
#[repr(C)] #[repr(C)]
#[cfg(bootstrap)]
struct VTable { struct VTable {
drop_in_place: fn(*mut ()), drop_in_place: fn(*mut ()),
size_of: usize, size_of: usize,
@ -194,13 +204,28 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
/// Returns the size of the type associated with this vtable. /// Returns the size of the type associated with this vtable.
#[inline] #[inline]
pub fn size_of(self) -> usize { 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. /// Returns the alignment of the type associated with this vtable.
#[inline] #[inline]
pub fn align_of(self) -> usize { 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` /// 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 --> $DIR/ub-incorrect-vtable.rs:19:14
| |
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; 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 error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:24:14 --> $DIR/ub-incorrect-vtable.rs:24:14
| |
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; 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 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> = 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 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) { = 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 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> = 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 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) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼ ╾─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`. 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 --> $DIR/ub-incorrect-vtable.rs:19:14
| |
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; 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 error[E0080]: evaluation of constant value failed
--> $DIR/ub-incorrect-vtable.rs:24:14 --> $DIR/ub-incorrect-vtable.rs:24:14
| |
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; 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 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> = 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 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) { = 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 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> = 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 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) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼ ╾───────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`. For more information about this error, try `rustc --explain E0080`.

View File

@ -18,27 +18,79 @@ trait Trait {}
const INVALID_VTABLE_ALIGNMENT: &dyn Trait = const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) }; unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
//~^ ERROR evaluation of constant value failed //~^ 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 = const INVALID_VTABLE_SIZE: &dyn Trait =
unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) }; unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
//~^ ERROR evaluation of constant value failed //~^ ERROR evaluation of constant value failed
//~| invalid vtable: size is bigger than largest supported object //~| does not point to a vtable
#[repr(transparent)] #[repr(transparent)]
struct W<T>(T); 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) {} fn drop_me(_: *mut usize) {}
const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> = const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) }; unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) };
//~^^ ERROR it is undefined behavior to use this value //~^^ 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> = const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) }; unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };
//~^^ ERROR it is undefined behavior to use this value //~^^ 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() {} 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 --> $DIR/ub-ref-ptr.rs:56:1
| |
LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:60:1
| |
LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:62:1
| |
LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:56:1
| |
LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:60:1
| |
LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:62:1
| |
LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; 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 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) { = 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 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) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾─alloc3──╼ ╾─alloc6──╼ │ ╾──╼╾──╼ ╾─alloc3──╼ ╾─alloc4──╼ │ ╾──╼╾──╼
} }
error: aborting due to previous error 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 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) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼ ╾───────alloc3────────╼ ╾───────alloc4────────╼ │ ╾──────╼╾──────╼
} }
error: aborting due to previous error 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 --> $DIR/ub-wide-ptr.rs:117:1
| |
LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; 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 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) { = 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 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))) }; 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 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) { = 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 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))) }; 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 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) { = note: the raw bytes of the constant (size: 8, align: 4) {
╾allocN─╼ 04 00 00 00 │ ╾──╼.... ╾allocN─╼ 04 00 00 00 │ ╾──╼....
} }
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:125:1 --> $DIR/ub-wide-ptr.rs:128:57
| |
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered unaligned vtable pointer in wide pointer | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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─╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:127:1 --> $DIR/ub-wide-ptr.rs:131:57
| |
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; 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) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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─╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:129:1 --> $DIR/ub-wide-ptr.rs:134:56
| |
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; 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) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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─╼ │ ╾──╼╾──╼
}
error[E0080]: it is undefined behavior to use this value 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]))) }; 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 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) { = 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 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) }; 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 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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─╼ │ ╾──╼╾──╼ ╾allocN─╼ ╾allocN─╼ │ ╾──╼╾──╼
} }
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:139:1 --> $DIR/ub-wide-ptr.rs:147:62
| |
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; 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 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
|
= 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 │ ╾──╼....
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:141:1 --> $DIR/ub-wide-ptr.rs:150:65
| |
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered too small vtable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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─╼ │ ╾──╼╾──╼
}
error[E0080]: could not evaluate static initializer 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)) 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 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)) 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 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 --> $DIR/ub-wide-ptr.rs:117:1
| |
LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; 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 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) { = 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 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))) }; 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 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) { = 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 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))) }; 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 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) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾──────allocN───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ ╾──────allocN───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
} }
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:125:1 --> $DIR/ub-wide-ptr.rs:128:57
| |
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered unaligned vtable pointer in wide pointer | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:127:1 --> $DIR/ub-wide-ptr.rs:131:57
| |
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; 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) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:129:1 --> $DIR/ub-wide-ptr.rs:134:56
| |
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; 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) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: it is undefined behavior to use this value 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]))) }; 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 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) { = 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 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) }; 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 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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───────╼ │ ╾──────╼╾──────╼ ╾──────allocN───────╼ ╾──────allocN───────╼ │ ╾──────╼╾──────╼
} }
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:139:1 --> $DIR/ub-wide-ptr.rs:147:62
| |
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; 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 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: null pointer is a dangling pointer (it has no provenance)
|
= 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 │ ╾──────╼........
}
error[E0080]: it is undefined behavior to use this value error[E0080]: evaluation of constant value failed
--> $DIR/ub-wide-ptr.rs:141:1 --> $DIR/ub-wide-ptr.rs:150:65
| |
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered too small vtable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a 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───────╼ │ ╾──────╼╾──────╼
}
error[E0080]: could not evaluate static initializer 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)) 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 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)) 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 error: aborting due to 32 previous errors

View File

@ -116,30 +116,40 @@ const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe {
// bad trait object // bad trait object
const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad trait object // bad trait object
const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad trait object // bad trait object
const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) };
//~^ ERROR it is undefined behavior to use this value //~^ 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])) }; 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])) }; 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])) }; 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]))) }; 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 //~^ ERROR it is undefined behavior to use this value
//~| expected a vtable
// bad data *inside* the trait object // bad data *inside* the trait object
const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) };
//~^ ERROR it is undefined behavior to use this value //~^ ERROR it is undefined behavior to use this value
//~| expected a boolean
// # raw trait object // # raw trait object
const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; 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)) }; 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 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. // 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 --> $DIR/issue-79690.rs:30:1
| |
LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; 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 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) { = note: the raw bytes of the constant (size: 16, align: 8) {
╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼ ╾───────alloc3────────╼ ╾───────alloc4────────╼ │ ╾──────╼╾──────╼
} }
error: aborting due to previous error 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 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) { = 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 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 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) { = note: the raw bytes of the constant (size: 4, align: 4) {
╾─alloc11─╼ │ ╾──╼ ╾─alloc10─╼ │ ╾──╼
} }
warning: skipping const checks 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 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) { = 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 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 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) { = note: the raw bytes of the constant (size: 8, align: 8) {
╾───────alloc11───────╼ │ ╾──────╼ ╾───────alloc10───────╼ │ ╾──────╼
} }
warning: skipping const checks warning: skipping const checks

View File

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

View File

@ -12,6 +12,7 @@
#[rustc_dump_vtable] #[rustc_dump_vtable]
trait A { trait A {
//~^ error vtable
fn foo_a(&self) {} fn foo_a(&self) {}
} }
@ -23,6 +24,7 @@ trait B {
#[rustc_dump_vtable] #[rustc_dump_vtable]
trait C: A + B { trait C: A + B {
//~^ error vtable
fn foo_c(&self) {} fn foo_c(&self) {}
} }
@ -115,8 +117,27 @@ impl M for S {}
impl N for S {} impl N for S {}
impl O for S {} impl O for S {}
fn foo(_: &dyn O) {} macro_rules! monomorphize_vtable {
($trait:ident) => {{
fn foo(_ : &dyn $trait) {}
foo(&S);
}}
}
fn main() { 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>), TraitVPtr(<S as N>),
Method(<S as O>::foo_o), 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 { 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>`: [ error: vtable entries for `<S as B>`: [
MetadataDropInPlace, MetadataDropInPlace,
MetadataSize, MetadataSize,
MetadataAlign, MetadataAlign,
Method(<S as B>::foo_b), Method(<S as B>::foo_b),
] ]
--> $DIR/vtable-multi-level.rs:19:1 --> $DIR/vtable-multi-level.rs:20:1
| |
LL | trait B { 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>`: [ error: vtable entries for `<S as D>`: [
MetadataDropInPlace, MetadataDropInPlace,
MetadataSize, MetadataSize,
MetadataAlign, MetadataAlign,
Method(<S as D>::foo_d), Method(<S as D>::foo_d),
] ]
--> $DIR/vtable-multi-level.rs:30:1 --> $DIR/vtable-multi-level.rs:32:1
| |
LL | trait D { LL | trait D {
| ^^^^^^^ | ^^^^^^^
@ -62,7 +87,7 @@ error: vtable entries for `<S as E>`: [
MetadataAlign, MetadataAlign,
Method(<S as E>::foo_e), Method(<S as E>::foo_e),
] ]
--> $DIR/vtable-multi-level.rs:36:1 --> $DIR/vtable-multi-level.rs:38:1
| |
LL | trait E { LL | trait E {
| ^^^^^^^ | ^^^^^^^
@ -76,7 +101,7 @@ error: vtable entries for `<S as F>`: [
TraitVPtr(<S as E>), TraitVPtr(<S as E>),
Method(<S as F>::foo_f), 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 { LL | trait F: D + E {
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
@ -87,7 +112,7 @@ error: vtable entries for `<S as H>`: [
MetadataAlign, MetadataAlign,
Method(<S as H>::foo_h), Method(<S as H>::foo_h),
] ]
--> $DIR/vtable-multi-level.rs:53:1 --> $DIR/vtable-multi-level.rs:55:1
| |
LL | trait H { LL | trait H {
| ^^^^^^^ | ^^^^^^^
@ -98,7 +123,7 @@ error: vtable entries for `<S as I>`: [
MetadataAlign, MetadataAlign,
Method(<S as I>::foo_i), Method(<S as I>::foo_i),
] ]
--> $DIR/vtable-multi-level.rs:59:1 --> $DIR/vtable-multi-level.rs:61:1
| |
LL | trait I { LL | trait I {
| ^^^^^^^ | ^^^^^^^
@ -112,7 +137,7 @@ error: vtable entries for `<S as J>`: [
TraitVPtr(<S as I>), TraitVPtr(<S as I>),
Method(<S as J>::foo_j), 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 { LL | trait J: H + I {
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
@ -123,7 +148,7 @@ error: vtable entries for `<S as K>`: [
MetadataAlign, MetadataAlign,
Method(<S as K>::foo_k), Method(<S as K>::foo_k),
] ]
--> $DIR/vtable-multi-level.rs:71:1 --> $DIR/vtable-multi-level.rs:73:1
| |
LL | trait K { LL | trait K {
| ^^^^^^^ | ^^^^^^^
@ -134,7 +159,7 @@ error: vtable entries for `<S as L>`: [
MetadataAlign, MetadataAlign,
Method(<S as L>::foo_l), Method(<S as L>::foo_l),
] ]
--> $DIR/vtable-multi-level.rs:77:1 --> $DIR/vtable-multi-level.rs:79:1
| |
LL | trait L { LL | trait L {
| ^^^^^^^ | ^^^^^^^
@ -148,7 +173,7 @@ error: vtable entries for `<S as M>`: [
TraitVPtr(<S as L>), TraitVPtr(<S as L>),
Method(<S as M>::foo_m), 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 { LL | trait M: K + L {
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
@ -169,10 +194,10 @@ error: vtable entries for `<S as N>`: [
TraitVPtr(<S as M>), TraitVPtr(<S as M>),
Method(<S as N>::foo_n), 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 { 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 {} impl C for S {}
fn foo(c: &dyn C) {} fn foo(c: &dyn C) {}
fn bar(c: &dyn B) {}
fn main() { fn main() {
foo(&S); foo(&S);
bar(&S);
} }