diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d57504deeab90..c5a11aaceaf9a 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -18,7 +18,7 @@ use rustc_target::spec::abi::Abi; use crate::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - OpTy, PlaceTy, Scalar, StackPopUnwind, + OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind, }; use super::error::*; @@ -443,6 +443,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Ok(()) } + #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer, + ) -> InterpResult<'tcx> { + Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into()) + } + #[inline(always)] fn init_frame_extra( ecx: &mut InterpCx<'mir, 'tcx, Self>, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 3ea3729dbcd17..92eeafc5df098 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -98,7 +98,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } pub fn misc_cast( - &self, + &mut self, src: &ImmTy<'tcx, M::PointerTag>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate> { @@ -139,7 +139,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { assert!(src.layout.is_zst()); let discr_layout = self.layout_of(discr.ty)?; - return Ok(self.cast_from_int_like(discr.val, discr_layout, cast_ty).into()); + + let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size()); + return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into()); } } Variants::Multiple { .. } => {} @@ -170,38 +172,65 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // # The remaining source values are scalar and "int-like". + let scalar = src.to_scalar()?; + + // If we are casting from a pointer to something + // that is not a pointer, mark the pointer as exposed + if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() { + let ptr = self.scalar_to_ptr(scalar)?; + + match ptr.into_pointer_or_addr() { + Ok(ptr) => { + M::expose_ptr(self, ptr)?; + } + Err(_) => { + // do nothing, exposing an invalid pointer + // has no meaning + } + }; + } - // For all remaining casts, we either - // (a) cast a raw ptr to usize, or - // (b) cast from an integer-like (including bool, char, enums). - // In both cases we want the bits. - let bits = src.to_scalar()?.to_bits(src.layout.size)?; - Ok(self.cast_from_int_like(bits, src.layout, cast_ty).into()) + Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into()) } - fn cast_from_int_like( + pub fn cast_from_int_like( &self, - v: u128, // raw bits (there is no ScalarTy so we separate data+layout) + scalar: Scalar, // input value (there is no ScalarTy so we separate data+layout) src_layout: TyAndLayout<'tcx>, cast_ty: Ty<'tcx>, - ) -> Scalar { + ) -> InterpResult<'tcx, Scalar> { // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. + + let v = scalar.to_bits(src_layout.size)?; let v = if signed { self.sign_extend(v, src_layout) } else { v }; trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); use rustc_middle::ty::TyKind::*; - match *cast_ty.kind() { - Int(_) | Uint(_) | RawPtr(_) => { + + Ok(match *cast_ty.kind() { + Int(_) | Uint(_) => { let size = match *cast_ty.kind() { Int(t) => Integer::from_int_ty(self, t).size(), Uint(t) => Integer::from_uint_ty(self, t).size(), - RawPtr(_) => self.pointer_size(), _ => bug!(), }; let v = size.truncate(v); Scalar::from_uint(v, size) } + RawPtr(_) => { + assert!(src_layout.ty.is_integral()); + + let size = self.pointer_size(); + let addr = u64::try_from(size.truncate(v)).unwrap(); + + let ptr = M::ptr_from_addr_cast(&self, addr); + if addr == 0 { + assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId"); + } + Scalar::from_maybe_pointer(ptr, self) + } + Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), @@ -214,7 +243,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Casts to bool are not permitted by rustc, no need to handle them here. _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), - } + }) } fn cast_from_float(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 827959113b907..dfb81a2afc4f7 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -905,7 +905,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!( "deallocating local {:?}: {:?}", local, - self.dump_alloc(ptr.provenance.unwrap().get_alloc_id()) + // Locals always have a `alloc_id` (they are never the result of a int2ptr). + self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap()) ); self.deallocate_ptr(ptr, None, MemoryKind::Stack)?; }; @@ -1013,9 +1014,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug } } - write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs)) + write!( + fmt, + ": {:?}", + self.ecx.dump_allocs(allocs.into_iter().filter_map(|x| x).collect()) + ) } - Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) { + Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) { Some(alloc_id) => write!( fmt, "by align({}) ref {:?}: {:?}", diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 7721485771b3b..a79751ccb55b4 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -286,19 +286,36 @@ pub trait Machine<'mir, 'tcx>: Sized { ) -> Pointer; /// "Int-to-pointer cast" - fn ptr_from_addr( + fn ptr_from_addr_cast( ecx: &InterpCx<'mir, 'tcx, Self>, addr: u64, ) -> Pointer>; + // FIXME: Transmuting an integer to a pointer should just always return a `None` + // provenance, but that causes problems with function pointers in Miri. + /// Hook for returning a pointer from a transmute-like operation on an addr. + fn ptr_from_addr_transmute( + ecx: &InterpCx<'mir, 'tcx, Self>, + addr: u64, + ) -> Pointer>; + + /// Marks a pointer as exposed, allowing it's provenance + /// to be recovered. "Pointer-to-int cast" + fn expose_ptr( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + ptr: Pointer, + ) -> InterpResult<'tcx>; + /// Convert a pointer with provenance into an allocation-offset pair /// and extra provenance info. /// /// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`. + /// + /// When this fails, that means the pointer does not point to a live allocation. fn ptr_get_alloc( ecx: &InterpCx<'mir, 'tcx, Self>, ptr: Pointer, - ) -> (AllocId, Size, Self::TagExtra); + ) -> Option<(AllocId, Size, Self::TagExtra)>; /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is @@ -480,7 +497,18 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn ptr_from_addr(_ecx: &InterpCx<$mir, $tcx, Self>, addr: u64) -> Pointer> { + fn ptr_from_addr_transmute( + _ecx: &InterpCx<$mir, $tcx, Self>, + addr: u64, + ) -> Pointer> { + Pointer::new(None, Size::from_bytes(addr)) + } + + #[inline(always)] + fn ptr_from_addr_cast( + _ecx: &InterpCx<$mir, $tcx, Self>, + addr: u64, + ) -> Pointer> { Pointer::new(None, Size::from_bytes(addr)) } @@ -488,9 +516,9 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn ptr_get_alloc( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer, - ) -> (AllocId, Size, Self::TagExtra) { + ) -> Option<(AllocId, Size, Self::TagExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); - (alloc_id, offset, ()) + Some((alloc_id, offset, ())) } } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index b1d7ab6a098be..33162a01ed201 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -770,7 +770,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if reachable.insert(id) { // This is a new allocation, add its relocations to `todo`. if let Some((_, alloc)) = self.memory.alloc_map.get(id) { - todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id())); + todo.extend( + alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()), + ); } } } @@ -805,7 +807,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, allocs_to_print: &mut VecDeque, alloc: &Allocation, ) -> std::fmt::Result { - for alloc_id in alloc.relocations().values().map(|tag| tag.get_alloc_id()) { + for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) { allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", display_allocation(tcx, alloc)) @@ -1142,7 +1144,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Err(ptr) => ptr.into(), Ok(bits) => { let addr = u64::try_from(bits).unwrap(); - let ptr = M::ptr_from_addr(&self, addr); + let ptr = M::ptr_from_addr_transmute(&self, addr); if addr == 0 { assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId"); } @@ -1182,10 +1184,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ptr: Pointer>, ) -> Result<(AllocId, Size, M::TagExtra), u64> { match ptr.into_pointer_or_addr() { - Ok(ptr) => { - let (alloc_id, offset, extra) = M::ptr_get_alloc(self, ptr); - Ok((alloc_id, offset, extra)) - } + Ok(ptr) => match M::ptr_get_alloc(self, ptr) { + Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)), + None => { + assert!(M::PointerTag::OFFSET_IS_ADDR); + let (_, addr) = ptr.into_parts(); + Err(addr.bytes()) + } + }, Err(addr) => Err(addr.bytes()), } } diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d627ddb39d00c..5c85c86107fea 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -741,17 +741,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Figure out which discriminant and variant this corresponds to. Ok(match *tag_encoding { TagEncoding::Direct => { + let scalar = tag_val.to_scalar()?; // Generate a specific error if `tag_val` is not an integer. // (`tag_bits` itself is only used for error messages below.) - let tag_bits = tag_val - .to_scalar()? + let tag_bits = scalar .try_to_int() .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))? .assert_bits(tag_layout.size); // Cast bits from tag layout to discriminant layout. - // After the checks we did above, this cannot fail. + // After the checks we did above, this cannot fail, as + // discriminants are int-like. let discr_val = - self.misc_cast(&tag_val, discr_layout.ty).unwrap().to_scalar().unwrap(); + self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap(); let discr_bits = discr_val.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. let index = match *op.layout.ty.kind() { diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c71aea417eca0..26da93b9dcebc 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -120,9 +120,11 @@ pub trait Provenance: Copy + fmt::Debug { where Self: Sized; - /// Provenance must always be able to identify the allocation this ptr points to. + /// If `OFFSET_IS_ADDR == false`, provenance must always be able to + /// identify the allocation this ptr points to (i.e., this must return `Some`). + /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`). /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.) - fn get_alloc_id(self) -> AllocId; + fn get_alloc_id(self) -> Option; } impl Provenance for AllocId { @@ -147,8 +149,8 @@ impl Provenance for AllocId { Ok(()) } - fn get_alloc_id(self) -> AllocId { - self + fn get_alloc_id(self) -> Option { + Some(self) } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 9cffdf2993ed5..eeee170f43f94 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -344,7 +344,8 @@ impl<'tcx, Tag: Provenance> Scalar { } else { // We know `offset` is relative, since `OFFSET_IS_ADDR == false`. let (tag, offset) = ptr.into_parts(); - Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id(), offset), sz)) + // Because `OFFSET_IS_ADDR == false`, this unwrap can never fail. + Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz)) } } } diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index f7535d338da40..6cb8c13946edf 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -28,7 +28,8 @@ use crate::MirPass; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame, ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy, - Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, + Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, + StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -285,6 +286,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> Ok(()) } + #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer, + ) -> InterpResult<'tcx> { + throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") + } + #[inline(always)] fn init_frame_extra( _ecx: &mut InterpCx<'mir, 'tcx, Self>, diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index aa898cfd3ba5e..a1ba1c0c0fc5d 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -30,8 +30,8 @@ use crate::MirLint; use rustc_const_eval::const_eval::ConstEvalErr; use rustc_const_eval::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar, - ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, + LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, + Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, }; /// The maximum number of bytes that we'll allocate space for a local or the return value. @@ -280,6 +280,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> Ok(()) } + #[inline(always)] + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: Pointer, + ) -> InterpResult<'tcx> { + throw_machine_stop_str!("exposing pointers isn't supported in ConstProp") + } + #[inline(always)] fn init_frame_extra( _ecx: &mut InterpCx<'mir, 'tcx, Self>, diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index 22314160c5e89..2beb531cc6890 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -14,7 +14,7 @@ static CMP: () = { static PTR_INT_CAST: () = { let x = &0 as *const _ as usize; //~^ ERROR could not evaluate static initializer - //~| unable to turn pointer into raw bytes + //~| "exposing pointers" needs an rfc before being allowed inside constants let _v = x == x; }; diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index 2764d10348a76..61d34e2e35df9 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -8,7 +8,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/ptr_arith.rs:15:13 | LL | let x = &0 as *const _ as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes + | ^^^^^^^^^^^^^^^^^^^^^^^ "exposing pointers" needs an rfc before being allowed inside constants error[E0080]: could not evaluate static initializer --> $DIR/ptr_arith.rs:23:14