From b2f58146b98d8197256123457bd68b38bb88549c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Feb 2023 16:00:54 +0100 Subject: [PATCH 1/6] basic dyn* support for Miri --- .../rustc_const_eval/src/interpret/cast.rs | 11 +- .../src/interpret/eval_context.rs | 2 +- .../rustc_const_eval/src/interpret/intern.rs | 2 +- .../rustc_const_eval/src/interpret/operand.rs | 20 ++- .../rustc_const_eval/src/interpret/place.rs | 67 ++++++--- .../src/interpret/terminator.rs | 127 ++++++++++++------ .../src/interpret/validity.rs | 23 ++-- .../rustc_const_eval/src/interpret/visitor.rs | 16 ++- .../branchless-select-i128-pointer.stderr | 4 +- .../miri/tests/fail/validity/dangling_ref1.rs | 2 +- .../tests/fail/validity/dangling_ref1.stderr | 4 +- src/tools/miri/tests/pass/dyn-star.rs | 118 ++++++++++++++++ src/tools/miri/tests/pass/dyn-star.stdout | 1 + .../consts/const-eval/raw-bytes.32bit.stderr | 4 +- .../consts/const-eval/raw-bytes.64bit.stderr | 4 +- tests/ui/consts/const-eval/ub-ref-ptr.stderr | 4 +- 16 files changed, 325 insertions(+), 84 deletions(-) create mode 100644 src/tools/miri/tests/pass/dyn-star.rs create mode 100644 src/tools/miri/tests/pass/dyn-star.stdout diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 68a91eabda7b9..2be5ed896ec80 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -312,6 +312,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + /// `src` is a *pointer to* a `source_ty`, and in `dest` we should store a pointer to th same + /// data at type `cast_ty`. fn unsize_into_ptr( &mut self, src: &OpTy<'tcx, M::Provenance>, @@ -335,7 +337,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.write_immediate(val, dest) } - (ty::Dynamic(data_a, ..), ty::Dynamic(data_b, ..)) => { + (ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => { let val = self.read_immediate(src)?; if data_a.principal() == data_b.principal() { // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables. @@ -359,7 +361,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => { - span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) + span_bug!( + self.cur_span(), + "invalid pointer unsizing {:?} -> {:?}", + src.layout.ty, + cast_ty + ) } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index c8bf769cfd8b7..24b157054d3e4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -632,7 +632,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Ok(Some((size, align))) } - ty::Dynamic(..) => { + ty::Dynamic(_, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). Ok(Some(self.get_vtable_size_and_align(vtable)?)) diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 301bfcef78a55..21ef1836188c0 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -242,7 +242,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory let mplace = self.ecx.ref_to_mplace(&value)?; assert_eq!(mplace.layout.ty, referenced_ty); // Handle trait object vtables. - if let ty::Dynamic(..) = + if let ty::Dynamic(_, _, ty::Dyn) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index ba41019aa938c..8d5192bca67e5 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -255,7 +255,22 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { } } - pub fn offset_with_meta( + /// Replace the layout of this operand. There's basically no sanity check that this makes sense, + /// you better know what you are doing! If this is an immediate, applying the wrong layout can + /// not just lead to invalid data, it can actually *shift the data around* since the offsets of + /// a ScalarPair are entirely determined by the layout, not the data. + pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self { + assert_eq!( + self.layout.size, layout.size, + "transmuting with a size change, that doesn't seem right" + ); + OpTy { layout, ..*self } + } + + /// Offset the operand in memory (if possible) and change its metadata. + /// + /// This can go wrong very easily if you give the wrong layout for the new place! + pub(super) fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -276,6 +291,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { } } + /// Offset the operand in memory (if possible). + /// + /// This can go wrong very easily if you give the wrong layout for the new place! pub fn offset( &self, offset: Size, diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 88485c06ed86c..244fa8030af73 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -26,6 +26,7 @@ pub enum MemPlaceMeta { } impl MemPlaceMeta { + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn unwrap_meta(self) -> Scalar { match self { Self::Meta(s) => s, @@ -147,12 +148,16 @@ impl MemPlace { } #[inline] - pub fn offset_with_meta<'tcx>( + pub(super) fn offset_with_meta<'tcx>( self, offset: Size, meta: MemPlaceMeta, cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { + debug_assert!( + !meta.has_meta() || self.meta.has_meta(), + "cannot use `offset_with_meta` to add metadata to a place" + ); Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta }) } } @@ -182,8 +187,11 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align } } + /// Offset the place in memory and change its metadata. + /// + /// This can go wrong very easily if you give the wrong layout for the new place! #[inline] - pub fn offset_with_meta( + pub(crate) fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -197,6 +205,9 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { }) } + /// Offset the place in memory. + /// + /// This can go wrong very easily if you give the wrong layout for the new place! pub fn offset( &self, offset: Size, @@ -241,14 +252,6 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { } } } - - #[inline] - pub(super) fn vtable(&self) -> Scalar { - match self.layout.ty.kind() { - ty::Dynamic(..) => self.mplace.meta.unwrap_meta(), - _ => bug!("vtable not supported on type {:?}", self.layout.ty), - } - } } // These are defined here because they produce a place. @@ -266,7 +269,12 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { - self.as_mplace_or_imm().left().unwrap() + self.as_mplace_or_imm().left().unwrap_or_else(|| { + bug!( + "OpTy of type {} was immediate when it was expected to be an MPlace", + self.layout.ty + ) + }) } } @@ -283,7 +291,12 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { - self.as_mplace_or_local().left().unwrap() + self.as_mplace_or_local().left().unwrap_or_else(|| { + bug!( + "PlaceTy of type {} was a local when it was expected to be an MPlace", + self.layout.ty + ) + }) } } @@ -807,11 +820,16 @@ where } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + /// Aso returns the vtable. pub(super) fn unpack_dyn_trait( &self, mplace: &MPlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer>)> { + assert!( + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + "`unpack_dyn_trait` only makes sense on `dyn*` types" + ); + let vtable = mplace.meta.unwrap_meta().to_pointer(self)?; let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; @@ -820,7 +838,26 @@ where layout, align: layout.align.abi, }; - Ok(mplace) + Ok((mplace, vtable)) + } + + /// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type. + /// Aso returns the vtable. + pub(super) fn unpack_dyn_star( + &self, + op: &OpTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer>)> { + assert!( + matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + "`unpack_dyn_star` only makes sense on `dyn*` types" + ); + let data = self.operand_field(&op, 0)?; + let vtable = self.operand_field(&op, 1)?; + let vtable = self.read_pointer(&vtable)?; + let (ty, _) = self.get_ptr_vtable(vtable)?; + let layout = self.layout_of(ty)?; + let data = data.transmute(layout); + Ok((data, vtable)) } } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index da320cd1cd5f0..96b0aee7ffb32 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -538,10 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively // unwrap those newtypes until we are there. let mut receiver = args[0].clone(); - let receiver_place = loop { + let receiver = loop { match receiver.layout.ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, - ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values + ty::Dynamic(..) | ty::Ref(..) | ty::RawPtr(..) => break receiver, _ => { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; @@ -567,39 +566,83 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } }; - // Obtain the underlying trait we are working on. - let receiver_tail = self - .tcx - .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); - let ty::Dynamic(data, ..) = receiver_tail.kind() else { - span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) - }; - // Get the required information from the vtable. - let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; - 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" - ); - } + // break self.deref_operand(&receiver)?.into(); + + // Obtain the underlying trait we are working on, and the adjusted receiver argument. + let recv_ty = receiver.layout.ty; + let (vptr, dyn_ty, adjusted_receiver) = match recv_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) + if matches!( + recv_ty.builtin_deref(true).unwrap().ty.kind(), + ty::Dynamic(_, _, ty::DynStar) + ) => + { + let receiver = self.deref_operand(&receiver)?; + let ty::Dynamic(data, ..) = receiver.layout.ty.kind() else { bug!() }; + let (recv, vptr) = self.unpack_dyn_star(&receiver.into())?; + 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" + ); + } + let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one + + (vptr, dyn_ty, recv.ptr) + } + ty::Dynamic(_, _, ty::DynStar) => { + // Not clear how to handle this, so far we assume the receiver is always a pointer. + span_bug!( + self.cur_span(), + "by-value calls on a `dyn*`... are those a thing?" + ); + } + _ => { + let receiver_place = match recv_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?, + ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate + _ => bug!(), + }; + // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. + // (For that reason we also cannot use `unpack_dyn_trait`.) + let receiver_tail = self.tcx.struct_tail_erasing_lifetimes( + receiver_place.layout.ty, + self.param_env, + ); + let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else { + span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) + }; + assert!(receiver_place.layout.is_unsized()); + + // Get the required information from the vtable. + let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; + 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" + ); + } + + // It might be surprising that we use a pointer as the receiver even if this + // is a by-val case; this works because by-val passing of an unsized `dyn + // Trait` to a function is actually desugared to a pointer. + (vptr, dyn_ty, receiver_place.ptr) + } + }; // Now determine the actual method to call. We can do that in two different ways and // compare them to ensure everything fits. let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else { throw_ub_format!("`dyn` call trying to call something that is not a method") }; + trace!("Virtual call dispatches to {fn_inst:#?}"); 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); @@ -614,17 +657,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(fn_inst, concrete_method); } - // `*mut receiver_place.layout.ty` is almost the layout that we - // want for args[0]: We have to project to field 0 because we want - // a thin pointer. - assert!(receiver_place.layout.is_unsized()); - let receiver_ptr_ty = self.tcx.mk_mut_ptr(receiver_place.layout.ty); - let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0); - // Adjust receiver argument. - args[0] = OpTy::from(ImmTy::from_immediate( - Scalar::from_maybe_pointer(receiver_place.ptr, self).into(), - this_receiver_ptr, - )); + // Adjust receiver argument. Layout can be any (thin) ptr. + args[0] = ImmTy::from_immediate( + Scalar::from_maybe_pointer(adjusted_receiver, self).into(), + self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?, + ) + .into(); trace!("Patched receiver operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call( @@ -653,15 +691,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // implementation fail -- a problem shared by rustc. let place = self.force_allocation(place)?; - let (instance, place) = match place.layout.ty.kind() { - ty::Dynamic(..) => { + let place = match place.layout.ty.kind() { + ty::Dynamic(_, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. - let place = self.unpack_dyn_trait(&place)?; - let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); - (instance, place) + self.unpack_dyn_trait(&place)?.0 + } + ty::Dynamic(_, _, ty::DynStar) => { + // Dropping a `dyn*`. Need to find actual drop fn. + self.unpack_dyn_star(&place.into())?.0.assert_mem_place() + } + _ => { + debug_assert_eq!( + instance, + ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty) + ); + place } - _ => (instance, place), }; + let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; let arg = ImmTy::from_immediate( diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 820ee32047424..e76d4c1728e90 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -23,18 +23,18 @@ use std::hash::Hash; // for the validation errors use super::UndefinedBehaviorInfo::*; use super::{ - CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, - MemPlaceMeta, OpTy, Scalar, ValueVisitor, + AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, + Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor, }; macro_rules! throw_validation_failure { - ($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{ + ($where:expr, { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )?) => {{ let mut msg = String::new(); msg.push_str("encountered "); - write!(&mut msg, $($what_fmt),+).unwrap(); + write!(&mut msg, $($what_fmt)*).unwrap(); $( msg.push_str(", but expected "); - write!(&mut msg, $($expected_fmt),+).unwrap(); + write!(&mut msg, $($expected_fmt)*).unwrap(); )? let path = rustc_middle::ty::print::with_no_trimmed_paths!({ let where_ = &$where; @@ -82,7 +82,7 @@ macro_rules! throw_validation_failure { /// macro_rules! try_validation { ($e:expr, $where:expr, - $( $( $p:pat_param )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)? + $( $( $p:pat_param )|+ => { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )? ),+ $(,)? ) => {{ match $e { Ok(x) => x, @@ -93,7 +93,7 @@ macro_rules! try_validation { InterpError::UndefinedBehavior($($p)|+) => throw_validation_failure!( $where, - { $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )? + { $( $what_fmt )* } $( expected { $( $expected_fmt )* } )? ) ),+, #[allow(unreachable_patterns)] @@ -335,7 +335,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { - ty::Dynamic(..) => { + ty::Dynamic(_, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer. let (_ty, _trait) = try_validation!( @@ -399,12 +399,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "an unaligned {kind} (required {} byte alignment but found {})", required.bytes(), - has.bytes() + has.bytes(), }, DanglingIntPointer(0, _) => { "a null {kind}" }, DanglingIntPointer(i, _) => - { "a dangling {kind} (address {i:#x} is unallocated)" }, + { + "a dangling {kind} ({pointer} has no provenance)", + pointer = Pointer::>::from_addr_invalid(*i), + }, PointerOutOfBounds { .. } => { "a dangling {kind} (going beyond the bounds of its allocation)" }, // This cannot happen during const-eval (because interning already detects diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index f9efc2418dbbc..7a14459399c43 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -284,7 +284,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> &self, ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // We `force_allocation` here so that `from_op` below can work. + // No need for `force_allocation` since we are just going to read from this. ecx.place_to_op(self) } @@ -421,15 +421,25 @@ macro_rules! make_value_visitor { // Special treatment for special types, where the (static) layout is not sufficient. match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(..) => { + ty::Dynamic(_, _, ty::Dyn) => { + // Dyn types. This is unsized, and the actual dynamic type of the data is given by the + // vtable stored in the place metadata. // unsized values are never immediate, so we can assert_mem_place let op = v.to_op_for_read(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); }, + ty::Dynamic(_, _, ty::DynStar) => { + // DynStar types. Very different from a dyn type (but strangely part of the + // same variant in `TyKind`): These are pairs where the 2nd component is the + // vtable, and the first component is the data (which must be ptr-sized). + let op = v.to_op_for_proj(self.ecx())?; + let data = self.ecx().unpack_dyn_star(&op)?.0; + return self.visit_field(&v, 0, &$value_trait::from_op(&data)); + } // Slices do not need special handling here: they have `Array` field // placement with length 0, so we enter the `Array` case below which // indirectly uses the metadata to determine the actual length. diff --git a/src/tools/miri/tests/fail/branchless-select-i128-pointer.stderr b/src/tools/miri/tests/fail/branchless-select-i128-pointer.stderr index 96f2ff3282c82..d68b4b8dfc675 100644 --- a/src/tools/miri/tests/fail/branchless-select-i128-pointer.stderr +++ b/src/tools/miri/tests/fail/branchless-select-i128-pointer.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address $HEX is unallocated) +error: Undefined Behavior: constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance) --> $DIR/branchless-select-i128-pointer.rs:LL:CC | LL | / transmute::<_, &str>( @@ -6,7 +6,7 @@ LL | | LL | | !mask & transmute::<_, TwoPtrs>("false !") LL | | | mask & transmute::<_, TwoPtrs>("true !"), LL | | ) - | |_____________^ constructing invalid value: encountered a dangling reference (address $HEX is unallocated) + | |_____________^ constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance) | = 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/validity/dangling_ref1.rs b/src/tools/miri/tests/fail/validity/dangling_ref1.rs index 6bf2d9295a370..fc3a9f344638f 100644 --- a/src/tools/miri/tests/fail/validity/dangling_ref1.rs +++ b/src/tools/miri/tests/fail/validity/dangling_ref1.rs @@ -3,5 +3,5 @@ use std::mem; fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) + let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference } diff --git a/src/tools/miri/tests/fail/validity/dangling_ref1.stderr b/src/tools/miri/tests/fail/validity/dangling_ref1.stderr index 01ef071e86930..830ab9ca501d8 100644 --- a/src/tools/miri/tests/fail/validity/dangling_ref1.stderr +++ b/src/tools/miri/tests/fail/validity/dangling_ref1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address 0x10 is unallocated) +error: Undefined Behavior: constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance) --> $DIR/dangling_ref1.rs:LL:CC | LL | let _x: &i32 = unsafe { mem::transmute(16usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x10 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance) | = 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/pass/dyn-star.rs b/src/tools/miri/tests/pass/dyn-star.rs new file mode 100644 index 0000000000000..4cac7048fdd19 --- /dev/null +++ b/src/tools/miri/tests/pass/dyn-star.rs @@ -0,0 +1,118 @@ +// Dyn* handling leads to some funky reentrancy in Stacked Borrows, for some reason +//@compile-flags: -Zmiri-disable-stacked-borrows +#![feature(dyn_star)] +#![allow(incomplete_features)] + +use std::fmt::{Debug, Display}; + +fn main() { + make_dyn_star(); + method(); + box_(); + dispatch_on_pin_mut(); + dyn_star_to_dyn(); + dyn_to_dyn_star(); +} + +fn dyn_star_to_dyn() { + let x: dyn* Debug = &42; + let x = Box::new(x) as Box; + assert_eq!("42", format!("{x:?}")); +} + +fn dyn_to_dyn_star() { + let x: Box = Box::new(42); + let x = &x as dyn* Debug; + assert_eq!("42", format!("{x:?}")); +} + +fn make_dyn_star() { + fn make_dyn_star_coercion(i: usize) { + let _dyn_i: dyn* Debug = i; + } + + fn make_dyn_star_explicit(i: usize) { + let _dyn_i: dyn* Debug = i as dyn* Debug; + } + + make_dyn_star_coercion(42); + make_dyn_star_explicit(42); +} + +fn method() { + trait Foo { + fn get(&self) -> usize; + } + + impl Foo for usize { + fn get(&self) -> usize { + *self + } + } + + fn invoke_dyn_star(i: dyn* Foo) -> usize { + i.get() + } + + fn make_and_invoke_dyn_star(i: usize) -> usize { + let dyn_i: dyn* Foo = i; + invoke_dyn_star(dyn_i) + } + + assert_eq!(make_and_invoke_dyn_star(42), 42); +} + +fn box_() { + fn make_dyn_star() -> dyn* Display { + Box::new(42) as dyn* Display + } + + let x = make_dyn_star(); + assert_eq!(format!("{x}"), "42"); +} + +fn dispatch_on_pin_mut() { + use std::future::Future; + + async fn foo(f: dyn* Future) { + println!("dispatch_on_pin_mut: value: {}", f.await); + } + + async fn async_main() { + foo(Box::pin(async { 1 })).await + } + + // ------------------------------------------------------------------------- // + // Implementation Details Below... + + use std::pin::Pin; + use std::task::*; + + pub fn noop_waker() -> Waker { + let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE); + + // SAFETY: the contracts for RawWaker and RawWakerVTable are upheld + unsafe { Waker::from_raw(raw) } + } + + const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + + unsafe fn noop_clone(_p: *const ()) -> RawWaker { + RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE) + } + + unsafe fn noop(_p: *const ()) {} + + let mut fut = async_main(); + + // Poll loop, just to test the future... + let waker = noop_waker(); + let ctx = &mut Context::from_waker(&waker); + + loop { + match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } { + Poll::Pending => {} + Poll::Ready(()) => break, + } + } +} diff --git a/src/tools/miri/tests/pass/dyn-star.stdout b/src/tools/miri/tests/pass/dyn-star.stdout new file mode 100644 index 0000000000000..e94427ee30531 --- /dev/null +++ b/src/tools/miri/tests/pass/dyn-star.stdout @@ -0,0 +1 @@ +dispatch_on_pin_mut: value: 1 diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index 91a426580c3cd..a0f8dd097c795 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:96:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] 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: 4, align: 4) { @@ -178,7 +178,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:99:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] 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: 4, align: 4) { diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index e4c5e62f6bd3a..9706f3ec2e01b 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:96:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] 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: 8) { @@ -178,7 +178,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/raw-bytes.rs:99:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] 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: 8) { diff --git a/tests/ui/consts/const-eval/ub-ref-ptr.stderr b/tests/ui/consts/const-eval/ub-ref-ptr.stderr index ce618802bd2c6..6bd367b646902 100644 --- a/tests/ui/consts/const-eval/ub-ref-ptr.stderr +++ b/tests/ui/consts/const-eval/ub-ref-ptr.stderr @@ -85,7 +85,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:43:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] 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: $SIZE, align: $ALIGN) { @@ -96,7 +96,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:46:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] 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: $SIZE, align: $ALIGN) { From 57056d7f8f79bf96ad99f562d00001451dc8fb4a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 Feb 2023 16:18:09 +0100 Subject: [PATCH 2/6] slightly beef up dyn-star-to-dyn test --- tests/ui/dyn-star/dyn-star-to-dyn.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/ui/dyn-star/dyn-star-to-dyn.rs b/tests/ui/dyn-star/dyn-star-to-dyn.rs index a6d9df9523af8..1d974b7ecb211 100644 --- a/tests/ui/dyn-star/dyn-star-to-dyn.rs +++ b/tests/ui/dyn-star/dyn-star-to-dyn.rs @@ -1,9 +1,17 @@ -// build-pass +// run-pass #![feature(dyn_star)] //~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes +use std::fmt::Debug; + fn main() { - let x: dyn* Send = &(); - let x = Box::new(x) as Box; + let x: dyn* Debug = &42; + let x = Box::new(x) as Box; + assert_eq!("42", format!("{x:?}")); + + // Also test opposite direction. + let x: Box = Box::new(42); + let x = &x as dyn* Debug; + assert_eq!("42", format!("{x:?}")); } From ad6dd60396d2a4e493e65ee5ba66562168d28cec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 Feb 2023 22:01:43 +0100 Subject: [PATCH 3/6] fix Stacked Borrows interaction with dyn* --- src/tools/miri/src/helpers.rs | 4 ++++ src/tools/miri/tests/pass/dyn-star.rs | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 5286023e0f64e..ed3dd741a8be0 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -478,6 +478,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else if matches!(v.layout.fields, FieldsShape::Union(..)) { // A (non-frozen) union. We fall back to whatever the type says. (self.unsafe_cell_action)(v) + } else if matches!(v.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)) { + // This needs to read the vtable pointer to proceed type-driven, but we don't + // want to reentrantly read from memory here. + (self.unsafe_cell_action)(v) } else { // We want to not actually read from memory for this visit. So, before // walking this value, we have to make sure it is not a diff --git a/src/tools/miri/tests/pass/dyn-star.rs b/src/tools/miri/tests/pass/dyn-star.rs index 4cac7048fdd19..16a8cec6cdae1 100644 --- a/src/tools/miri/tests/pass/dyn-star.rs +++ b/src/tools/miri/tests/pass/dyn-star.rs @@ -1,5 +1,3 @@ -// Dyn* handling leads to some funky reentrancy in Stacked Borrows, for some reason -//@compile-flags: -Zmiri-disable-stacked-borrows #![feature(dyn_star)] #![allow(incomplete_features)] From 472059d854c8c901321a8aa4d12e861bb064e437 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 20 Feb 2023 14:07:19 +0000 Subject: [PATCH 4/6] Deduplicate some logic between dyn* and dyn --- .../src/interpret/terminator.rs | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 96b0aee7ffb32..94ac3b6ebad86 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -571,26 +571,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Obtain the underlying trait we are working on, and the adjusted receiver argument. let recv_ty = receiver.layout.ty; - let (vptr, dyn_ty, adjusted_receiver) = match recv_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) - if matches!( - recv_ty.builtin_deref(true).unwrap().ty.kind(), - ty::Dynamic(_, _, ty::DynStar) - ) => - { - let receiver = self.deref_operand(&receiver)?; - let ty::Dynamic(data, ..) = receiver.layout.ty.kind() else { bug!() }; - let (recv, vptr) = self.unpack_dyn_star(&receiver.into())?; - 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" - ); - } - let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one - - (vptr, dyn_ty, recv.ptr) - } + let receiver_place = match recv_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?, + ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate ty::Dynamic(_, _, ty::DynStar) => { // Not clear how to handle this, so far we assume the receiver is always a pointer. span_bug!( @@ -598,37 +581,45 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { "by-value calls on a `dyn*`... are those a thing?" ); } - _ => { - let receiver_place = match recv_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?, - ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate - _ => bug!(), - }; - // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. - // (For that reason we also cannot use `unpack_dyn_trait`.) - let receiver_tail = self.tcx.struct_tail_erasing_lifetimes( - receiver_place.layout.ty, - self.param_env, + _ => bug!(), + }; + let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = + receiver_place.layout.ty.kind() + { + let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?; + 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" ); - let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else { + } + let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one + + (vptr, dyn_ty, recv.ptr) + } else { + // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. + // (For that reason we also cannot use `unpack_dyn_trait`.) + let receiver_tail = self + .tcx + .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); + let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else { span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) }; - assert!(receiver_place.layout.is_unsized()); - - // Get the required information from the vtable. - let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; - 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" - ); - } + assert!(receiver_place.layout.is_unsized()); - // It might be surprising that we use a pointer as the receiver even if this - // is a by-val case; this works because by-val passing of an unsized `dyn - // Trait` to a function is actually desugared to a pointer. - (vptr, dyn_ty, receiver_place.ptr) + // Get the required information from the vtable. + let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?; + 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" + ); } + + // It might be surprising that we use a pointer as the receiver even if this + // is a by-val case; this works because by-val passing of an unsized `dyn + // Trait` to a function is actually desugared to a pointer. + (vptr, dyn_ty, receiver_place.ptr) }; // Now determine the actual method to call. We can do that in two different ways and From 82f1e341a365d8e830ea4decdf7842f627d2b435 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 20 Feb 2023 14:07:43 +0000 Subject: [PATCH 5/6] Remove commented out code --- compiler/rustc_const_eval/src/interpret/terminator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 94ac3b6ebad86..151497cc0ebae 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -567,8 +567,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; - // break self.deref_operand(&receiver)?.into(); - // Obtain the underlying trait we are working on, and the adjusted receiver argument. let recv_ty = receiver.layout.ty; let receiver_place = match recv_ty.kind() { From 054c76d6580eb84139cb619c3dff8865828a9fbc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 20 Feb 2023 14:56:44 +0000 Subject: [PATCH 6/6] Revert most of the custom place computation, it was unnecessary. --- .../src/interpret/terminator.rs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 151497cc0ebae..f1bbdcbe39b7d 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -538,9 +538,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively // unwrap those newtypes until we are there. let mut receiver = args[0].clone(); - let receiver = loop { + let receiver_place = loop { match receiver.layout.ty.kind() { - ty::Dynamic(..) | ty::Ref(..) | ty::RawPtr(..) => break receiver, + ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, + ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values + ty::Dynamic(.., ty::DynStar) => { + // Not clear how to handle this, so far we assume the receiver is always a pointer. + span_bug!( + self.cur_span(), + "by-value calls on a `dyn*`... are those a thing?" + ); + } _ => { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; @@ -568,19 +576,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Obtain the underlying trait we are working on, and the adjusted receiver argument. - let recv_ty = receiver.layout.ty; - let receiver_place = match recv_ty.kind() { - ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?, - ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate - ty::Dynamic(_, _, ty::DynStar) => { - // Not clear how to handle this, so far we assume the receiver is always a pointer. - span_bug!( - self.cur_span(), - "by-value calls on a `dyn*`... are those a thing?" - ); - } - _ => bug!(), - }; let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() {