From ffa70dd11eb7b3cfa73c58d312c1595a3b993440 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 22 Sep 2024 20:00:01 -0400 Subject: [PATCH] Check vtable projections for validity in miri --- .../rustc_codegen_cranelift/src/constant.rs | 8 +-- compiler/rustc_codegen_gcc/src/common.rs | 4 +- compiler/rustc_codegen_llvm/src/common.rs | 4 +- compiler/rustc_const_eval/messages.ftl | 4 +- compiler/rustc_const_eval/src/errors.rs | 18 ++---- .../rustc_const_eval/src/interpret/cast.rs | 6 +- .../rustc_const_eval/src/interpret/memory.rs | 14 ++--- .../rustc_const_eval/src/interpret/traits.rs | 56 +++++++++++++------ .../src/interpret/validity.rs | 8 +-- .../rustc_middle/src/mir/interpret/error.rs | 12 ++-- .../rustc_middle/src/mir/interpret/mod.rs | 11 ++-- compiler/rustc_middle/src/mir/pretty.rs | 7 +-- compiler/rustc_monomorphize/src/collector.rs | 4 +- compiler/rustc_passes/src/reachable.rs | 7 ++- .../rustc_smir/src/rustc_smir/convert/mir.rs | 5 +- .../tests/fail/dyn-call-trait-mismatch.rs | 2 +- .../tests/fail/dyn-call-trait-mismatch.stderr | 4 +- .../tests/fail/dyn-upcast-trait-mismatch.rs | 2 +- .../fail/dyn-upcast-trait-mismatch.stderr | 4 +- ...i-3905-transmute-vtable-projection-term.rs | 18 ++++++ ...05-transmute-vtable-projection-term.stderr | 15 +++++ .../fail/validity/wrong-dyn-trait.stderr | 4 +- 22 files changed, 135 insertions(+), 82 deletions(-) create mode 100644 src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs create mode 100644 src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.stderr diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 0ba163f50aec5..4848269caa76a 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -161,13 +161,13 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_func_in_func(func_id, &mut fx.bcx.func); fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, vtable) => { let data_id = data_id_for_vtable( fx.tcx, &mut fx.constants_cx, fx.module, ty, - trait_ref, + vtable.principal(), ); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); @@ -456,8 +456,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(target_alloc) => { data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } - GlobalAlloc::VTable(ty, trait_ref) => { - data_id_for_vtable(tcx, cx, module, ty, trait_ref) + GlobalAlloc::VTable(ty, vtable) => { + data_id_for_vtable(tcx, cx, module, ty, vtable.principal()) } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index b7ed34858b5c4..b0990901b5378 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -224,10 +224,10 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { value } GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, vtable) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .global_alloc(self.tcx.vtable_allocation((ty, vtable.principal()))) .unwrap_memory(); let init = const_alloc_to_gcc(self, alloc); self.static_addr_of(init, alloc.inner().align, None) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 508c2d1a820e2..57a140be07c55 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -290,10 +290,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.get_fn_addr(instance.polymorphize(self.tcx)), self.data_layout().instruction_address_space, ), - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, vtable) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) + .global_alloc(self.tcx.vtable_allocation((ty, vtable.principal()))) .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc, /*static*/ false); let value = self.static_addr_of(init, alloc.inner().align, None); diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 2dc9d57e3b5c7..5fed0b1ca29e8 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -198,7 +198,7 @@ const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable const_eval_invalid_vtable_trait = - using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected + using vtable for `{$allocated_vtable}` but `{$expected_vtable}` was expected const_eval_lazy_lock = consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` @@ -459,7 +459,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer -const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}` +const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_vtable}`, but encountered `{$allocated_vtable}` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 06ae5b30be066..dd4b010f44eb7 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -525,12 +525,9 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } - InvalidVTableTrait { expected_trait, vtable_trait } => { - diag.arg("expected_trait", expected_trait.to_string()); - diag.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), - ); + InvalidVTableTrait { expected_vtable, allocated_vtable } => { + diag.arg("expected_vtable", expected_vtable.to_string()); + diag.arg("allocated_vtable", allocated_vtable.to_string()); } PointerUseAfterFree(alloc_id, msg) => { diag.arg("alloc_id", alloc_id) @@ -780,12 +777,9 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { DanglingPtrNoProvenance { pointer, .. } => { err.arg("pointer", pointer); } - InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => { - err.arg("ref_trait", ref_trait.to_string()); - err.arg( - "vtable_trait", - vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), - ); + InvalidMetaWrongTrait { allocated_vtable, expected_vtable } => { + err.arg("allocated_vtable", allocated_vtable.to_string()); + err.arg("expected_vtable", expected_vtable.to_string()); } NullPtr { .. } | ConstRefToMutable diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index e8c9f145eea55..0bc37632137dc 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -128,7 +128,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { CastKind::DynStar => { if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?; + let vtable = self.get_vtable_ptr(src.layout.ty, data)?; let vtable = Scalar::from_maybe_pointer(vtable, self); let data = self.read_immediate(src)?.to_scalar(); let _assert_pointer_like = data.to_pointer(self)?; @@ -446,12 +446,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Get the destination trait vtable and return that. - let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; + let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } (_, &ty::Dynamic(data, _, ty::Dyn)) => { // Initial cast from sized to dyn trait - let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?; + let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index b1d1dab7c162b..9bc06043796ab 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -943,12 +943,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if offset.bytes() != 0 { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) } - let Some(GlobalAlloc::VTable(ty, vtable_trait)) = self.tcx.try_get_global_alloc(alloc_id) + let Some(GlobalAlloc::VTable(ty, allocated_vtable)) = + self.tcx.try_get_global_alloc(alloc_id) else { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) }; - if let Some(expected_trait) = expected_trait { - self.check_vtable_for_type(vtable_trait, expected_trait)?; + if let Some(expected_vtable) = expected_trait { + self.check_vtable_for_type(allocated_vtable, expected_vtable)?; } Ok(ty) } @@ -1113,11 +1114,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::Function { instance, .. }) => { write!(fmt, " (fn: {instance})")?; } - Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { - write!(fmt, " (vtable: impl {trait_ref} for {ty})")?; - } - Some(GlobalAlloc::VTable(ty, None)) => { - write!(fmt, " (vtable: impl for {ty})")?; + Some(GlobalAlloc::VTable(ty, vtable)) => { + write!(fmt, " (vtable: impl {vtable} for {ty})")?; } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index cd4faf06655fe..975ca5bcf0d6a 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt, VtblEntry}; +use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry}; use rustc_target::abi::{Align, Size}; use tracing::trace; @@ -18,19 +18,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn get_vtable_ptr( &self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + vtable: &'tcx ty::List>, ) -> InterpResult<'tcx, Pointer>> { - trace!("get_vtable(trait_ref={:?})", poly_trait_ref); + trace!("get_vtable(ty={ty:?}, vtable={vtable:?})"); - let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref)); + let (ty, vtable) = self.tcx.erase_regions((ty, vtable)); // All vtables must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, ty)?; - ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; + ensure_monomorphic_enough(*self.tcx, vtable)?; let salt = M::get_global_alloc_salt(self, None); - let vtable_symbolic_allocation = - self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref, salt); + let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, vtable, salt); let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } @@ -64,17 +63,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// expected trait type. pub(super) fn check_vtable_for_type( &self, - vtable_trait: Option>, - expected_trait: &'tcx ty::List>, + allocated_vtable: &'tcx ty::List>, + expected_vtable: &'tcx ty::List>, ) -> InterpResult<'tcx> { - let eq = match (expected_trait.principal(), vtable_trait) { - (Some(a), Some(b)) => self.eq_in_param_env(a, b), - (None, None) => true, - _ => false, - }; - if !eq { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + let mut sorted_allocated_v: Vec<_> = allocated_vtable.without_auto_traits().collect(); + let mut sorted_expected_v: Vec<_> = expected_vtable.without_auto_traits().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + sorted_allocated_v.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_allocated_v.dedup(); + sorted_expected_v.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder())); + sorted_expected_v.dedup(); + + if sorted_allocated_v.len() != sorted_expected_v.len() { + throw_ub!(InvalidVTableTrait { allocated_vtable, expected_vtable }); + } + + for (a_pred, b_pred) in std::iter::zip(sorted_allocated_v, sorted_expected_v) { + let is_eq = match (a_pred.skip_binder(), b_pred.skip_binder()) { + ( + ty::ExistentialPredicate::Trait(a_data), + ty::ExistentialPredicate::Trait(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + ( + ty::ExistentialPredicate::Projection(a_data), + ty::ExistentialPredicate::Projection(b_data), + ) => self.eq_in_param_env(a_pred.rebind(a_data), b_pred.rebind(b_data)), + + _ => false, + }; + if !is_eq { + throw_ub!(InvalidVTableTrait { allocated_vtable, expected_vtable }); + } } + + // FIXME: Should we validate auto traits here? + Ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index b20df7ac9c1d1..29c0ca1a44199 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -452,8 +452,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { self.path, Ub(DanglingIntPointer{ .. } | InvalidVTablePointer(..)) => InvalidVTablePtr { value: format!("{vtable}") }, - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { allocated_vtable, expected_vtable }) => { + InvalidMetaWrongTrait { allocated_vtable, expected_vtable } }, ); } @@ -1280,8 +1280,8 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, // It's not great to catch errors here, since we can't give a very good path, // but it's better than ICEing. - Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { - InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + Ub(InvalidVTableTrait { allocated_vtable, expected_vtable }) => { + InvalidMetaWrongTrait { allocated_vtable, expected_vtable: *expected_vtable } }, ); } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8c89c15f9611d..5bbf4245c8e37 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -365,8 +365,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidVTablePointer(Pointer), /// Using a vtable for the wrong trait. InvalidVTableTrait { - expected_trait: &'tcx ty::List>, - vtable_trait: Option>, + /// The vtable that was actually allocated into global memory. + allocated_vtable: &'tcx ty::List>, + /// The vtable that was expected at the point in MIR that it was accessed. + expected_vtable: &'tcx ty::List>, }, /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), @@ -479,8 +481,10 @@ pub enum ValidationErrorKind<'tcx> { value: String, }, InvalidMetaWrongTrait { - expected_trait: &'tcx ty::List>, - vtable_trait: Option>, + /// The vtable that was actually allocated into global memory. + allocated_vtable: &'tcx ty::List>, + /// The vtable that was expected at the point in MIR that it was accessed. + expected_vtable: &'tcx ty::List>, }, InvalidMetaSliceTooLarge { ptr_kind: PointerKind, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 91e71c12cae45..20306a2377613 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -232,9 +232,8 @@ impl<'s> AllocDecodingSession<'s> { } AllocDiscriminant::VTable => { trace!("creating vtable alloc ID"); - let ty = as Decodable>::decode(decoder); - let poly_trait_ref = - > as Decodable>::decode(decoder); + let ty = Decodable::decode(decoder); + let poly_trait_ref = Decodable::decode(decoder); trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT) } @@ -259,7 +258,7 @@ pub enum GlobalAlloc<'tcx> { /// The alloc ID is used as a function pointer. Function { instance: Instance<'tcx> }, /// This alloc ID points to a symbolic (not-reified) vtable. - VTable(Ty<'tcx>, Option>), + VTable(Ty<'tcx>, &'tcx ty::List>), /// The alloc ID points to a "lazy" static variable that did not get computed (yet). /// This is also used to break the cycle in recursive statics. Static(DefId), @@ -293,7 +292,7 @@ impl<'tcx> GlobalAlloc<'tcx> { #[inline] pub fn unwrap_vtable(&self) -> (Ty<'tcx>, Option>) { match *self { - GlobalAlloc::VTable(ty, poly_trait_ref) => (ty, poly_trait_ref), + GlobalAlloc::VTable(ty, vtable) => (ty, vtable.principal()), _ => bug!("expected vtable, got {:?}", self), } } @@ -398,7 +397,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn reserve_and_set_vtable_alloc( self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + poly_trait_ref: &'tcx ty::List>, salt: usize, ) -> AllocId { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref), salt) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d66d0be10095f..a08341747a644 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1536,11 +1536,8 @@ pub fn write_allocations<'tcx>( // gracefully handle it and allow buggy rustc to be debugged via allocation printing. None => write!(w, " (deallocated)")?, Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?, - Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => { - write!(w, " (vtable: impl {trait_ref} for {ty})")? - } - Some(GlobalAlloc::VTable(ty, None)) => { - write!(w, " (vtable: impl for {ty})")? + Some(GlobalAlloc::VTable(ty, vtable)) => { + write!(w, " (vtable: impl {vtable} for {ty})")? } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { match tcx.eval_static_initializer(did) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index c3f228962005b..edd73ca2824d1 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1175,8 +1175,8 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt output.push(create_fn_mono_item(tcx, instance, DUMMY_SP)); } } - GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + GlobalAlloc::VTable(ty, vtable) => { + let alloc_id = tcx.vtable_allocation((ty, vtable.principal())); collect_alloc(tcx, alloc_id, output) } } diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 8d9e00b9f3c8f..5d391b1f904a4 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -318,14 +318,17 @@ impl<'tcx> ReachableContext<'tcx> { )); self.visit(instance.args); } - GlobalAlloc::VTable(ty, trait_ref) => { + GlobalAlloc::VTable(ty, vtable) => { self.visit(ty); // Manually visit to actually see the trait's `DefId`. Type visitors won't see it - if let Some(trait_ref) = trait_ref { + if let Some(trait_ref) = vtable.principal() { let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder(); self.visit_def_id(def_id, "", &""); self.visit(args); } + + // FIXME: are we just skipping the auto traits here? + // What is this reachability *for*? } GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c442ca861d35b..06878ec5f70df 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -712,8 +712,9 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { mir::interpret::GlobalAlloc::Function { instance, .. } => { GlobalAlloc::Function(instance.stable(tables)) } - mir::interpret::GlobalAlloc::VTable(ty, trait_ref) => { - GlobalAlloc::VTable(ty.stable(tables), trait_ref.stable(tables)) + mir::interpret::GlobalAlloc::VTable(ty, vtable) => { + // FIXME: Should we record the whole vtable? + GlobalAlloc::VTable(ty.stable(tables), vtable.principal().stable(tables)) } mir::interpret::GlobalAlloc::Static(def) => { GlobalAlloc::Static(tables.static_def(*def)) diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs index 982d57b737202..7ac619e09abf6 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -16,5 +16,5 @@ impl T1 for i32 { fn main() { let r = Box::new(0) as Box; let r2: Box = unsafe { std::mem::transmute(r) }; - r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected + r2.method2(); //~ERROR: using vtable for `T1` but `T2` was expected } diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr index 3680a84fac271..37d102c87134d 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected +error: Undefined Behavior: using vtable for `T1` but `T2` was expected --> tests/fail/dyn-call-trait-mismatch.rs:LL:CC | LL | r2.method2(); - | ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected + | ^^^^^^^^^^^^ using vtable for `T1` but `T2` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs index 85d7582d112ae..f450e7e652c51 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs @@ -63,6 +63,6 @@ fn main() { let baz: &dyn Baz = &1; let baz_fake: *const dyn Bar = std::mem::transmute(baz); let _err = baz_fake as *const dyn Foo; - //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected + //~^ERROR: using vtable for `Baz` but `Bar` was expected } } diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr index 2129fe66e9c02..b40f83e86541a 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected +error: Undefined Behavior: using vtable for `Baz` but `Bar` was expected --> tests/fail/dyn-upcast-trait-mismatch.rs:LL:CC | LL | let _err = baz_fake as *const dyn Foo; - | ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected + | ^^^^^^^^ using vtable for `Baz` but `Bar` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs b/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs new file mode 100644 index 0000000000000..1478abedee0d8 --- /dev/null +++ b/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs @@ -0,0 +1,18 @@ +trait Trait { + type Assoc; + fn foo(&self) -> Self::Assoc; +} + +impl Trait for T { + type Assoc = T; + fn foo(&self) -> T { *self } +} + +fn main() { + let v: Box> = Box::new(2); + let v: Box> = unsafe { std::mem::transmute(v) }; //~ERROR: wrong trait + + if v.foo() { + println!("huh"); + } +} diff --git a/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.stderr b/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.stderr new file mode 100644 index 0000000000000..06953f56ea37a --- /dev/null +++ b/src/tools/miri/tests/fail/issue-miri-3905-transmute-vtable-projection-term.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + --> tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs:LL:CC + | +LL | let v: Box> = unsafe { std::mem::transmute(v) }; + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/issue-miri-3905-transmute-vtable-projection-term.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr index 4be3fb52bdb7f..45c882bebdfc6 100644 --- a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send` --> tests/fail/validity/wrong-dyn-trait.rs:LL:CC | LL | let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; - | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `std::marker::Send` | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information