From d4f7dd670226a4235ea4cf900c14eb9c6a536843 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 12 Jul 2021 18:22:15 +0200 Subject: [PATCH 01/16] CTFE/Miri engine Pointer type overhaul: make Scalar-to-Pointer conversion infallible This resolves all the problems we had around "normalizing" the representation of a Scalar in case it carries a Pointer value: we can just use Pointer if we want to have a value taht we are sure is already normalized. --- compiler/rustc_codegen_llvm/src/common.rs | 7 +- compiler/rustc_codegen_llvm/src/consts.rs | 2 +- .../src/mir/interpret/allocation.rs | 59 ++-- .../rustc_middle/src/mir/interpret/error.rs | 22 +- .../rustc_middle/src/mir/interpret/mod.rs | 2 +- .../rustc_middle/src/mir/interpret/pointer.rs | 161 +++++---- .../rustc_middle/src/mir/interpret/value.rs | 180 +++++----- compiler/rustc_middle/src/mir/mod.rs | 6 +- compiler/rustc_middle/src/ty/consts/kind.rs | 5 +- compiler/rustc_middle/src/ty/print/pretty.rs | 11 +- compiler/rustc_middle/src/ty/relate.rs | 2 +- compiler/rustc_mir/src/const_eval/error.rs | 7 - .../rustc_mir/src/const_eval/eval_queries.rs | 32 +- compiler/rustc_mir/src/const_eval/machine.rs | 12 +- compiler/rustc_mir/src/const_eval/mod.rs | 6 +- compiler/rustc_mir/src/interpret/cast.rs | 2 +- .../rustc_mir/src/interpret/eval_context.rs | 94 +++--- compiler/rustc_mir/src/interpret/intern.rs | 28 +- .../rustc_mir/src/interpret/intrinsics.rs | 43 +-- .../interpret/intrinsics/caller_location.rs | 2 +- compiler/rustc_mir/src/interpret/machine.rs | 72 ++-- compiler/rustc_mir/src/interpret/memory.rs | 319 ++++++++++-------- compiler/rustc_mir/src/interpret/operand.rs | 116 ++++--- compiler/rustc_mir/src/interpret/operator.rs | 6 +- compiler/rustc_mir/src/interpret/place.rs | 241 +++++++------ compiler/rustc_mir/src/interpret/step.rs | 8 +- .../rustc_mir/src/interpret/terminator.rs | 22 +- compiler/rustc_mir/src/interpret/traits.rs | 18 +- compiler/rustc_mir/src/interpret/validity.rs | 26 +- compiler/rustc_mir/src/interpret/visitor.rs | 6 +- .../rustc_mir/src/monomorphize/collector.rs | 8 +- .../rustc_mir/src/transform/const_prop.rs | 14 +- .../transform/simplify_comparison_integral.rs | 2 +- compiler/rustc_mir/src/util/pretty.rs | 18 +- 34 files changed, 837 insertions(+), 722 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index df5ad8ecc271d..a73932ee1b57f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -244,7 +244,8 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } Scalar::Ptr(ptr) => { - let (base_addr, base_addr_space) = match self.tcx.global_alloc(ptr.alloc_id) { + let (alloc_id, offset) = ptr.into_parts(); + let (base_addr, base_addr_space) = match self.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { let init = const_alloc_to_llvm(self, alloc); let value = match alloc.mutability { @@ -252,7 +253,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { _ => self.static_addr_of(init, alloc.align, None), }; if !self.sess().fewer_names() { - llvm::set_value_name(value, format!("{:?}", ptr.alloc_id).as_bytes()); + llvm::set_value_name(value, format!("{:?}", alloc_id).as_bytes()); } (value, AddressSpace::DATA) } @@ -269,7 +270,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { let llval = unsafe { llvm::LLVMConstInBoundsGEP( self.const_bitcast(base_addr, self.type_i8p_ext(base_addr_space)), - &self.const_usize(ptr.offset.bytes()), + &self.const_usize(offset.bytes()), 1, ) }; diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index e50d5506e222f..71a387bfcacd9 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -25,7 +25,7 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll let pointer_size = dl.pointer_size.bytes() as usize; let mut next_offset = 0; - for &(offset, ((), alloc_id)) in alloc.relocations().iter() { + for &(offset, alloc_id) in alloc.relocations().iter() { let offset = offset.bytes(); assert_eq!(offset as usize as u64, offset); let offset = offset as usize; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 75cbb55239c8b..f9840e35bc60d 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -25,7 +25,7 @@ use crate::ty; /// module provides higher-level access. #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] -pub struct Allocation { +pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. bytes: Vec, @@ -154,25 +154,17 @@ impl Allocation { } } -impl Allocation<()> { - /// Add Tag and Extra fields - pub fn with_tags_and_extra( +impl Allocation { + /// Convert Tag and add Extra fields + pub fn with_prov_and_extra( self, - mut tagger: impl FnMut(AllocId) -> T, - extra: E, - ) -> Allocation { + mut tagger: impl FnMut(AllocId) -> Tag, + extra: Extra, + ) -> Allocation { Allocation { bytes: self.bytes, relocations: Relocations::from_presorted( - self.relocations - .iter() - // The allocations in the relocations (pointers stored *inside* this allocation) - // all get the base pointer tag. - .map(|&(offset, ((), alloc))| { - let tag = tagger(alloc); - (offset, (tag, alloc)) - }) - .collect(), + self.relocations.iter().map(|&(offset, tag)| (offset, tagger(tag))).collect(), ), init_mask: self.init_mask, align: self.align, @@ -339,8 +331,8 @@ impl Allocation { self.check_relocations(cx, range)?; } else { // Maybe a pointer. - if let Some(&(tag, alloc_id)) = self.relocations.get(&range.start) { - let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits), tag); + if let Some(&prov) = self.relocations.get(&range.start) { + let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(ScalarMaybeUninit::Scalar(ptr.into())); } } @@ -371,9 +363,12 @@ impl Allocation { } }; - let bytes = match val.to_bits_or_ptr(range.size, cx) { - Err(val) => u128::from(val.offset.bytes()), - Ok(data) => data, + let (bytes, provenance) = match val.to_bits_or_ptr(range.size, cx) { + Err(val) => { + let (provenance, offset) = val.into_parts(); + (u128::from(offset.bytes()), Some(provenance)) + } + Ok(data) => (data, None), }; let endian = cx.data_layout().endian; @@ -381,8 +376,8 @@ impl Allocation { write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also write a relocation. - if let Scalar::Ptr(val) = val { - self.relocations.insert(range.start, (val.tag, val.alloc_id)); + if let Some(provenance) = provenance { + self.relocations.insert(range.start, provenance); } Ok(()) @@ -392,11 +387,7 @@ impl Allocation { /// Relocations. impl Allocation { /// Returns all relocations overlapping with the given pointer-offset pair. - pub fn get_relocations( - &self, - cx: &impl HasDataLayout, - range: AllocRange, - ) -> &[(Size, (Tag, AllocId))] { + pub fn get_relocations(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Tag)] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1); @@ -582,24 +573,24 @@ impl Allocation { } } -/// Relocations. +/// "Relocations" stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -pub struct Relocations(SortedMap); +pub struct Relocations(SortedMap); -impl Relocations { +impl Relocations { pub fn new() -> Self { Relocations(SortedMap::new()) } // The caller must guarantee that the given relocations are already sorted // by address and contain no duplicates. - pub fn from_presorted(r: Vec<(Size, (Tag, Id))>) -> Self { + pub fn from_presorted(r: Vec<(Size, Tag)>) -> Self { Relocations(SortedMap::from_presorted_elements(r)) } } impl Deref for Relocations { - type Target = SortedMap; + type Target = SortedMap; fn deref(&self) -> &Self::Target { &self.0 @@ -614,7 +605,7 @@ impl DerefMut for Relocations { /// A partial, owned list of relocations to transfer into another allocation. pub struct AllocationRelocations { - relative_relocations: Vec<(Size, (Tag, AllocId))>, + relative_relocations: Vec<(Size, Tag)>, } impl Allocation { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index ab9239585c4aa..29caf01af1f5b 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -238,7 +238,9 @@ pub enum UndefinedBehaviorInfo<'tcx> { PointerUseAfterFree(AllocId), /// Used a pointer outside the bounds it is valid for. PointerOutOfBounds { - ptr: Pointer, + alloc_id: AllocId, + offset: Size, + size: Size, msg: CheckInAllocMsg, allocation_size: Size, }, @@ -307,19 +309,19 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg), UnterminatedCString(p) => write!( f, - "reading a null-terminated string starting at {} with no null found before end of allocation", + "reading a null-terminated string starting at {:?} with no null found before end of allocation", p, ), PointerUseAfterFree(a) => { write!(f, "pointer to {} was dereferenced after this allocation got freed", a) } - PointerOutOfBounds { ptr, msg, allocation_size } => write!( + PointerOutOfBounds { alloc_id, offset, size, msg, allocation_size } => write!( f, - "{}pointer must be in-bounds at offset {}, \ - but is outside bounds of {} which has size {}", + "{}pointer must be in-bounds for {} bytes at offset {}, but {} has size {}", msg, - ptr.offset.bytes(), - ptr.alloc_id, + size.bytes(), + offset.bytes(), + alloc_id, allocation_size.bytes() ), DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => { @@ -348,13 +350,13 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { } InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val), InvalidFunctionPointer(p) => { - write!(f, "using {} as function pointer but it does not point to a function", p) + write!(f, "using {:?} as function pointer but it does not point to a function", p) } InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), InvalidUninitBytes(Some((alloc, access))) => write!( f, - "reading {} byte{} of memory starting at {}, \ - but {} byte{} {} uninitialized starting at {}, \ + "reading {} byte{} of memory starting at {:?}, \ + but {} byte{} {} uninitialized starting at {:?}, \ and this operation requires initialized memory", access.access_size.bytes(), pluralize!(access.access_size.bytes()), diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 14bdb0a5a2d50..44fa94c89c5cd 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -127,7 +127,7 @@ pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMay pub use self::allocation::{alloc_range, AllocRange, Allocation, InitMask, Relocations}; -pub use self::pointer::{Pointer, PointerArithmetic}; +pub use self::pointer::{Pointer, PointerArithmetic, Provenance}; /// Uniquely identifies one of the following: /// - A constant diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 8774b48fb3e40..c9dc96fc88e02 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -83,55 +83,74 @@ pub trait PointerArithmetic: HasDataLayout { impl PointerArithmetic for T {} -/// Represents a pointer in the Miri engine. -/// -/// `Pointer` is generic over the `Tag` associated with each pointer, -/// which is used to do provenance tracking during execution. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] -#[derive(HashStable)] -pub struct Pointer { - pub alloc_id: AllocId, - pub offset: Size, - pub tag: Tag, +/// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is +/// mostly opaque; the `Machine` trait extends it with some more operations that also have access to +/// some global state. +pub trait Provenance: Copy { + /// Says whether the `offset` field of `Pointer` is the actual physical address. + /// If `true, ptr-to-int casts work by simply discarding the provenance. + /// If `false`, ptr-to-int casts are not supported. + const OFFSET_IS_ADDR: bool; + + /// Determines how a pointer should be printed. + fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result + where + Self: Sized; + + /// "Erasing" a tag converts it to the default tag type if possible. Used only for formatting purposes! + fn erase_for_fmt(self) -> AllocId; } -static_assert_size!(Pointer, 16); +impl Provenance for AllocId { + // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*, + // so ptr-to-int casts are not possible (since we do not know the global physical offset). + const OFFSET_IS_ADDR: bool = false; -/// Print the address of a pointer (without the tag) -fn print_ptr_addr(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Forward `alternate` flag to `alloc_id` printing. - if f.alternate() { - write!(f, "{:#?}", ptr.alloc_id)?; - } else { - write!(f, "{:?}", ptr.alloc_id)?; + fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Forward `alternate` flag to `alloc_id` printing. + if f.alternate() { + write!(f, "{:#?}", ptr.provenance)?; + } else { + write!(f, "{:?}", ptr.provenance)?; + } + // Print offset only if it is non-zero. + if ptr.offset.bytes() > 0 { + write!(f, "+0x{:x}", ptr.offset.bytes())?; + } + Ok(()) } - // Print offset only if it is non-zero. - if ptr.offset.bytes() > 0 { - write!(f, "+0x{:x}", ptr.offset.bytes())?; + + fn erase_for_fmt(self) -> AllocId { + self } - Ok(()) } +/// Represents a pointer in the Miri engine. +/// +/// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] +#[derive(HashStable)] +pub struct Pointer { + pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Tag` type) + pub provenance: Tag, +} + +//FIXME static_assert_size!(Pointer, 16); + // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -// We have to use `Debug` output for the tag, because `()` does not implement -// `Display` so we cannot specialize that. -impl fmt::Debug for Pointer { - default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - print_ptr_addr(self, f)?; - write!(f, "[{:?}]", self.tag) - } -} -// Specialization for no tag -impl fmt::Debug for Pointer<()> { +impl fmt::Debug for Pointer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - print_ptr_addr(self, f) + Tag::fmt(self, f) } } -impl fmt::Display for Pointer { +impl fmt::Debug for Pointer> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) + match self.provenance { + Some(tag) => Tag::fmt(&Pointer::new(tag, self.offset), f), + None => write!(f, "0x{:x}", self.offset.bytes()), + } } } @@ -143,37 +162,66 @@ impl From for Pointer { } } -impl Pointer<()> { +impl From> for Pointer> { #[inline(always)] - pub fn new(alloc_id: AllocId, offset: Size) -> Self { - Pointer { alloc_id, offset, tag: () } + fn from(ptr: Pointer) -> Self { + let (tag, offset) = ptr.into_parts(); + Pointer::new(Some(tag), offset) + } +} + +impl Pointer> { + pub fn into_pointer_or_offset(self) -> Result, Size> { + match self.provenance { + Some(tag) => Ok(Pointer::new(tag, self.offset)), + None => Err(self.offset), + } } #[inline(always)] - pub fn with_tag(self, tag: Tag) -> Pointer { - Pointer::new_with_tag(self.alloc_id, self.offset, tag) + pub fn map_erase_for_fmt(self) -> Pointer> + where + Tag: Provenance, + { + Pointer { offset: self.offset, provenance: self.provenance.map(Provenance::erase_for_fmt) } } } impl<'tcx, Tag> Pointer { #[inline(always)] - pub fn new_with_tag(alloc_id: AllocId, offset: Size, tag: Tag) -> Self { - Pointer { alloc_id, offset, tag } + pub fn new(provenance: Tag, offset: Size) -> Self { + Pointer { provenance, offset } + } + + /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Tag`! + /// This function must only be used in the implementation of `Machine::ptr_get_alloc`, + /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`. + #[inline(always)] + pub fn into_parts(self) -> (Tag, Size) { + (self.provenance, self.offset) + } + + #[inline(always)] + pub fn erase_for_fmt(self) -> Pointer + where + Tag: Provenance, + { + Pointer { offset: self.offset, provenance: self.provenance.erase_for_fmt() } } #[inline] pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer::new_with_tag( - self.alloc_id, - Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), - self.tag, - )) + Ok(Pointer { + offset: Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?), + ..self + }) } #[inline] pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes()); - (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) + let ptr = Pointer { offset: Size::from_bytes(res), ..self }; + (ptr, over) } #[inline(always)] @@ -183,26 +231,21 @@ impl<'tcx, Tag> Pointer { #[inline] pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - Ok(Pointer::new_with_tag( - self.alloc_id, - Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), - self.tag, - )) + Ok(Pointer { + offset: Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?), + ..self + }) } #[inline] pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i); - (Pointer::new_with_tag(self.alloc_id, Size::from_bytes(res), self.tag), over) + let ptr = Pointer { offset: Size::from_bytes(res), ..self }; + (ptr, over) } #[inline(always)] pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { self.overflowing_signed_offset(i, cx).0 } - - #[inline(always)] - pub fn erase_tag(self) -> Pointer { - Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () } - } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 66ff6990e8cdb..29692c07f031c 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -10,7 +10,9 @@ use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; -use super::{AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic}; +use super::{ + AllocId, AllocRange, Allocation, InterpResult, Pointer, PointerArithmetic, Provenance, +}; /// Represents the result of const evaluation via the `eval_to_allocation` query. #[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)] @@ -47,12 +49,6 @@ pub enum ConstValue<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(ConstValue<'_>, 32); -impl From for ConstValue<'tcx> { - fn from(s: Scalar) -> Self { - Self::Scalar(s) - } -} - impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> { type Lifted = ConstValue<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { @@ -70,7 +66,7 @@ impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> { impl<'tcx> ConstValue<'tcx> { #[inline] - pub fn try_to_scalar(&self) -> Option { + pub fn try_to_scalar(&self) -> Option> { match *self { ConstValue::ByRef { .. } | ConstValue::Slice { .. } => None, ConstValue::Scalar(val) => Some(val), @@ -120,9 +116,12 @@ impl<'tcx> ConstValue<'tcx> { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 16 bytes in /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes /// of a simple value or a pointer into another `Allocation` +/// +/// These variants would be private if there was a convenient way to achieve that in Rust. +/// Do *not* match on a `Scalar`! Use the various `to_*` methods instead. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] #[derive(HashStable)] -pub enum Scalar { +pub enum Scalar { /// The raw bytes of a simple value. Int(ScalarInt), @@ -133,11 +132,11 @@ pub enum Scalar { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Scalar, 24); +//FIXME static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl fmt::Debug for Scalar { +impl fmt::Debug for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr) => write!(f, "{:?}", ptr), @@ -146,11 +145,11 @@ impl fmt::Debug for Scalar { } } -impl fmt::Display for Scalar { +impl fmt::Display for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr) => write!(f, "pointer to {}", ptr), - Scalar::Int { .. } => fmt::Debug::fmt(self, f), + Scalar::Ptr(ptr) => write!(f, "pointer to {:?}", ptr), + Scalar::Int(int) => write!(f, "{:?}", int), } } } @@ -169,38 +168,38 @@ impl From for Scalar { } } -impl Scalar<()> { - /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise. - /// - /// Used by `MemPlace::replace_tag`. - #[inline] - pub fn with_tag(self, new_tag: Tag) -> Scalar { - match self { - Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_tag(new_tag)), - Scalar::Int(int) => Scalar::Int(int), - } +impl From> for Scalar { + #[inline(always)] + fn from(ptr: Pointer) -> Self { + Scalar::Ptr(ptr) + } +} + +impl From for Scalar { + #[inline(always)] + fn from(ptr: ScalarInt) -> Self { + Scalar::Int(ptr) } } impl<'tcx, Tag> Scalar { pub const ZST: Self = Scalar::Int(ScalarInt::ZST); - /// Erase the tag from the scalar, if any. - /// - /// Used by error reporting code to avoid having the error type depend on `Tag`. - #[inline] - pub fn erase_tag(self) -> Scalar { - match self { - Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()), - Scalar::Int(int) => Scalar::Int(int), - } - } - #[inline] pub fn null_ptr(cx: &impl HasDataLayout) -> Self { Scalar::Int(ScalarInt::null(cx.data_layout().pointer_size)) } + /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer). + pub fn from_maybe_pointer(ptr: Pointer>, cx: &impl HasDataLayout) -> Self { + match ptr.into_parts() { + (Some(tag), offset) => Scalar::Ptr(Pointer::new(tag, offset)), + (None, offset) => { + Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) + } + } + } + #[inline(always)] fn ptr_op( self, @@ -332,10 +331,11 @@ impl<'tcx, Tag> Scalar { Scalar::Int(f.into()) } - /// This is very rarely the method you want! You should dispatch on the type - /// and use `force_bits`/`assert_bits`/`force_ptr`/`assert_ptr`. + /// This is almost certainly not the method you want! You should dispatch on the type + /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed. + /// /// This method only exists for the benefit of low-level memory operations - /// as well as the implementation of the `force_*` methods. + /// as well as the implementation of the above methods. #[inline] pub fn to_bits_or_ptr( self, @@ -352,28 +352,13 @@ impl<'tcx, Tag> Scalar { } } - /// This method is intentionally private! - /// It is just a helper for other methods in this file. - #[inline] - fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { - assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - match self { - Scalar::Int(int) => int.to_bits(target_size).map_err(|size| { - err_ub!(ScalarSizeMismatch { - target_size: target_size.bytes(), - data_size: size.bytes(), - }) - .into() - }), - Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes), - } - } - + /// Do not call this method! It does not do ptr-to-int casts when needed. #[inline(always)] pub fn assert_bits(self, target_size: Size) -> u128 { - self.to_bits(target_size).expect("expected Raw bits but got a Pointer") + self.assert_int().assert_bits(target_size) } + /// Do not call this method! It does not do ptr-to-int casts when needed. #[inline] pub fn assert_int(self) -> ScalarInt { match self { @@ -382,6 +367,7 @@ impl<'tcx, Tag> Scalar { } } + /// Do not call this method! It does not do int-to-ptr casts when needed. #[inline] pub fn assert_ptr(self) -> Pointer { match self { @@ -401,6 +387,44 @@ impl<'tcx, Tag> Scalar { pub fn is_ptr(self) -> bool { matches!(self, Scalar::Ptr(_)) } +} + +impl<'tcx, Tag: Provenance> Scalar { + /// Erase the tag from the scalar, if any. + /// + /// Used by error reporting code to avoid having the error type depend on `Tag`. + #[inline] + pub fn erase_for_fmt(self) -> Scalar { + match self { + Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_for_fmt()), + Scalar::Int(int) => Scalar::Int(int), + } + } + + /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you + /// likely want to use instead. + /// + /// Will perform ptr-to-int casts if needed and possible. + #[inline] + pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { + assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); + match self { + Scalar::Int(int) => int.to_bits(target_size).map_err(|size| { + err_ub!(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: size.bytes(), + }) + .into() + }), + Scalar::Ptr(ptr) => { + if Tag::OFFSET_IS_ADDR { + Ok(ptr.offset.bytes().into()) + } else { + throw_unsup!(ReadPointerAsBytes) + } + } + } + } pub fn to_bool(self) -> InterpResult<'tcx, bool> { let val = self.to_u8()?; @@ -507,28 +531,14 @@ impl<'tcx, Tag> Scalar { } } -impl From> for Scalar { - #[inline(always)] - fn from(ptr: Pointer) -> Self { - Scalar::Ptr(ptr) - } -} - -impl From for Scalar { - #[inline(always)] - fn from(ptr: ScalarInt) -> Self { - Scalar::Int(ptr) - } -} - #[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)] -pub enum ScalarMaybeUninit { +pub enum ScalarMaybeUninit { Scalar(Scalar), Uninit, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ScalarMaybeUninit, 24); +//FIXME static_assert_size!(ScalarMaybeUninit, 24); impl From> for ScalarMaybeUninit { #[inline(always)] @@ -546,7 +556,7 @@ impl From> for ScalarMaybeUninit { // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. -impl fmt::Debug for ScalarMaybeUninit { +impl fmt::Debug for ScalarMaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, ""), @@ -555,7 +565,7 @@ impl fmt::Debug for ScalarMaybeUninit { } } -impl fmt::Display for ScalarMaybeUninit { +impl fmt::Display for ScalarMaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ScalarMaybeUninit::Uninit => write!(f, "uninitialized bytes"), @@ -564,23 +574,25 @@ impl fmt::Display for ScalarMaybeUninit { } } -impl<'tcx, Tag> ScalarMaybeUninit { - /// Erase the tag from the scalar, if any. - /// - /// Used by error reporting code to avoid having the error type depend on `Tag`. +impl ScalarMaybeUninit { #[inline] - pub fn erase_tag(self) -> ScalarMaybeUninit { + pub fn check_init(self) -> InterpResult<'static, Scalar> { match self { - ScalarMaybeUninit::Scalar(s) => ScalarMaybeUninit::Scalar(s.erase_tag()), - ScalarMaybeUninit::Uninit => ScalarMaybeUninit::Uninit, + ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), + ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), } } +} +impl<'tcx, Tag: Provenance> ScalarMaybeUninit { + /// Erase the tag from the scalar, if any. + /// + /// Used by error reporting code to avoid having the error type depend on `Tag`. #[inline] - pub fn check_init(self) -> InterpResult<'static, Scalar> { + pub fn erase_for_fmt(self) -> ScalarMaybeUninit { match self { - ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), - ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), + ScalarMaybeUninit::Scalar(s) => ScalarMaybeUninit::Scalar(s.erase_for_fmt()), + ScalarMaybeUninit::Uninit => ScalarMaybeUninit::Uninit, } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ed813c054c263..9fc02a590c35c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,7 +3,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use crate::mir::coverage::{CodeRegion, CoverageKind}; -use crate::mir::interpret::{Allocation, GlobalAlloc, Scalar}; +use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::adjustment::PointerCast; use crate::ty::codec::{TyDecoder, TyEncoder}; @@ -2095,7 +2095,7 @@ impl<'tcx> Operand<'tcx> { Operand::Constant(box Constant { span, user_ty: None, - literal: ConstantKind::Val(val.into(), ty), + literal: ConstantKind::Val(ConstValue::Scalar(val), ty), }) } @@ -2458,7 +2458,7 @@ pub enum ConstantKind<'tcx> { impl Constant<'tcx> { pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { match self.literal.const_for_ty()?.val.try_to_scalar() { - Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { + Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.provenance) { GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); Some(def_id) diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 875d8d00a93d3..f2db95d162b88 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,7 +1,6 @@ use std::convert::TryInto; -use crate::mir::interpret::ConstValue; -use crate::mir::interpret::Scalar; +use crate::mir::interpret::{AllocId, ConstValue, Scalar}; use crate::mir::Promoted; use crate::ty::subst::{InternalSubsts, SubstsRef}; use crate::ty::ParamEnv; @@ -59,7 +58,7 @@ impl<'tcx> ConstKind<'tcx> { } #[inline] - pub fn try_to_scalar(self) -> Option { + pub fn try_to_scalar(self) -> Option> { self.try_to_value()?.try_to_scalar() } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 21d5baced7687..25058d2cef715 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -987,6 +987,7 @@ pub trait PrettyPrinter<'tcx>: ) -> Result { define_scoped_cx!(self); + let (alloc_id, offset) = ptr.into_parts(); match ty.kind() { // Byte strings (&[u8; N]) ty::Ref( @@ -1002,10 +1003,10 @@ pub trait PrettyPrinter<'tcx>: .. }, _, - ) => match self.tcx().get_global_alloc(ptr.alloc_id) { + ) => match self.tcx().get_global_alloc(alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { let len = int.assert_bits(self.tcx().data_layout.pointer_size); - let range = AllocRange { start: ptr.offset, size: Size::from_bytes(len) }; + let range = AllocRange { start: offset, size: Size::from_bytes(len) }; if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), range) { p!(pretty_print_byte_str(byte_str)) } else { @@ -1020,7 +1021,7 @@ pub trait PrettyPrinter<'tcx>: ty::FnPtr(_) => { // FIXME: We should probably have a helper method to share code with the "Byte strings" // printing above (which also has to handle pointers to all sorts of things). - match self.tcx().get_global_alloc(ptr.alloc_id) { + match self.tcx().get_global_alloc(alloc_id) { Some(GlobalAlloc::Function(instance)) => { self = self.typed_value( |this| this.print_value_path(instance.def_id(), instance.substs), @@ -1068,8 +1069,8 @@ pub trait PrettyPrinter<'tcx>: ty::Char if char::try_from(int).is_ok() => { p!(write("{:?}", char::try_from(int).unwrap())) } - // Raw pointers - ty::RawPtr(_) | ty::FnPtr(_) => { + // Pointer types + ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => { let data = int.assert_bits(self.tcx().data_layout.pointer_size); self = self.typed_value( |mut this| { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 3f426b13688fe..5d1cc1242760c 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -597,7 +597,7 @@ fn check_const_value_eq>( } (ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => { a_val == b_val - || match (tcx.global_alloc(a_val.alloc_id), tcx.global_alloc(b_val.alloc_id)) { + || match (tcx.global_alloc(a_val.provenance), tcx.global_alloc(b_val.provenance)) { (GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => { a_instance == b_instance } diff --git a/compiler/rustc_mir/src/const_eval/error.rs b/compiler/rustc_mir/src/const_eval/error.rs index 17e8ab2a4da63..5da1681662577 100644 --- a/compiler/rustc_mir/src/const_eval/error.rs +++ b/compiler/rustc_mir/src/const_eval/error.rs @@ -16,7 +16,6 @@ use crate::interpret::{ #[derive(Clone, Debug)] pub enum ConstEvalErrKind { NeedsRfc(String), - PtrToIntCast, ConstAccessesStatic, ModifiedGlobal, AssertFailure(AssertKind), @@ -49,12 +48,6 @@ impl fmt::Display for ConstEvalErrKind { NeedsRfc(ref msg) => { write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) } - PtrToIntCast => { - write!( - f, - "cannot cast pointer to integer because it was not created by cast from integer" - ) - } ConstAccessesStatic => write!(f, "constant accesses static"), ModifiedGlobal => { write!(f, "modifying a static's initial value from another static's initializer") diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index c1c26d4e810d0..00d73d42cfcd8 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -136,19 +136,18 @@ pub(super) fn op_to_const<'tcx>( // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or // structs containing such. - op.try_as_mplace(ecx) + op.try_as_mplace() }; - let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr { - Scalar::Ptr(ptr) => { - let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: ptr.offset } + let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr.into_parts() { + (Some(alloc_id), offset) => { + let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); + ConstValue::ByRef { alloc, offset } } - Scalar::Int(int) => { + (None, offset) => { assert!(mplace.layout.is_zst()); assert_eq!( - int.assert_bits(ecx.tcx.data_layout.pointer_size) - % u128::from(mplace.layout.align.abi.bytes()), + offset.bytes() % mplace.layout.align.abi.bytes(), 0, "this MPlaceTy must come from a validated constant, thus we can assume the \ alignment is correct", @@ -162,14 +161,14 @@ pub(super) fn op_to_const<'tcx>( Err(imm) => match *imm { Immediate::Scalar(x) => match x { ScalarMaybeUninit::Scalar(s) => ConstValue::Scalar(s), - ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place(ecx)), + ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()), }, Immediate::ScalarPair(a, b) => { - let (data, start) = match a.check_init().unwrap() { - Scalar::Ptr(ptr) => { - (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) + let (data, start) = match ecx.scalar_to_ptr(a.check_init().unwrap()).into_parts() { + (Some(alloc_id), offset) => { + (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) } - Scalar::Int { .. } => ( + (None, _offset) => ( ecx.tcx.intern_const_alloc(Allocation::from_bytes_byte_aligned_immutable( b"" as &[u8], )), @@ -369,6 +368,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( inner = true; } }; + let alloc_id = mplace.ptr.provenance.unwrap(); if let Err(error) = validation { // Validation failed, report an error. This is always a hard error. let err = ConstEvalErr::new(&ecx, error, None); @@ -381,9 +381,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( "the raw bytes of the constant ({}", display_allocation( *ecx.tcx, - ecx.tcx - .global_alloc(mplace.ptr.assert_ptr().alloc_id) - .unwrap_memory() + ecx.tcx.global_alloc(alloc_id).unwrap_memory() ) )); diag.emit(); @@ -391,7 +389,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( )) } else { // Convert to raw constant - Ok(ConstAlloc { alloc_id: mplace.ptr.assert_ptr().alloc_id, ty: mplace.layout.ty }) + Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) } } } diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index f8b66badb8a4c..dcabc91f7a8e9 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -16,8 +16,8 @@ use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; use crate::interpret::{ - self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, Memory, - OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind, + self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, OpTy, + PlaceTy, Scalar, StackPopUnwind, }; use super::error::*; @@ -59,7 +59,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> { pub steps_remaining: usize, /// The virtual call stack. - pub(crate) stack: Vec>, + pub(crate) stack: Vec>, } #[derive(Copy, Clone, Debug)] @@ -184,7 +184,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { // is in bounds, because if they are in bounds, the pointer can't be null. // Inequality with integers other than null can never be known for sure. (Scalar::Int(int), Scalar::Ptr(ptr)) | (Scalar::Ptr(ptr), Scalar::Int(int)) => { - int.is_null() && !self.memory.ptr_may_be_null(ptr) + int.is_null() && !self.memory.ptr_may_be_null(ptr.into()) } // FIXME: return `true` for at least some comparisons where we can reliably // determine the result of runtime inequality tests at compile-time. @@ -356,10 +356,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Err(ConstEvalErrKind::Abort(msg).into()) } - fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { - Err(ConstEvalErrKind::PtrToIntCast.into()) - } - fn binary_ptr_op( _ecx: &InterpCx<'mir, 'tcx, Self>, _bin_op: mir::BinOp, diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index 6a514e9f62fce..8254e5790013b 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -35,7 +35,7 @@ pub(crate) fn const_caller_location( if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { bug!("intern_const_alloc_recursive should not error in this case") } - ConstValue::Scalar(loc_place.ptr) + ConstValue::Scalar(Scalar::Ptr(loc_place.ptr.into_pointer_or_offset().unwrap())) } /// Convert an evaluated constant to a type level constant @@ -179,9 +179,9 @@ pub(crate) fn deref_const<'tcx>( let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); let op = ecx.const_to_op(val, None).unwrap(); let mplace = ecx.deref_operand(&op).unwrap(); - if let Scalar::Ptr(ptr) = mplace.ptr { + if let Some(alloc_id) = mplace.ptr.provenance { assert_eq!( - tcx.get_global_alloc(ptr.alloc_id).unwrap().unwrap_memory().mutability, + tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().mutability, Mutability::Not, "deref_const cannot be used with mutable allocations as \ that could allow pattern matching to observe mutable statics", diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 848b44d13aadf..2821728b1d57a 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -175,7 +175,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // (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 = self.force_bits(src.to_scalar()?, src.layout.size)?; + let bits = src.to_scalar()?.to_bits(src.layout.size)?; Ok(self.cast_from_scalar(bits, src.layout, cast_ty).into()) } diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index 8cd459265dfc5..ef021435f46b5 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -8,7 +8,6 @@ use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use rustc_middle::ich::StableHashingContext; use rustc_middle::mir; -use rustc_middle::mir::interpret::{GlobalId, InterpResult, Pointer, Scalar}; use rustc_middle::ty::layout::{self, TyAndLayout}; use rustc_middle::ty::{ self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, @@ -18,8 +17,9 @@ use rustc_span::{Pos, Span}; use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout}; use super::{ - Immediate, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, - PlaceTy, ScalarMaybeUninit, StackPopJump, + AllocId, GlobalId, Immediate, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, + MemoryKind, Operand, Place, PlaceTy, Pointer, Provenance, Scalar, ScalarMaybeUninit, + StackPopJump, }; use crate::transform::validate::equal_up_to_regions; use crate::util::storage::AlwaysLiveLocals; @@ -80,7 +80,7 @@ impl Drop for SpanGuard { } /// A stack frame. -pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { +pub struct Frame<'mir, 'tcx, Tag = AllocId, Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// @@ -161,7 +161,7 @@ pub enum StackPopCleanup { /// State of a local variable including a memoized layout #[derive(Clone, PartialEq, Eq, HashStable)] -pub struct LocalState<'tcx, Tag = ()> { +pub struct LocalState<'tcx, Tag = AllocId> { pub value: LocalValue, /// Don't modify if `Some`, this is only used to prevent computing the layout twice #[stable_hasher(ignore)] @@ -169,8 +169,8 @@ pub struct LocalState<'tcx, Tag = ()> { } /// Current value of a local variable -#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these -pub enum LocalValue { +#[derive(Copy, Clone, PartialEq, Eq, HashStable)] +pub enum LocalValue { /// This local is not currently alive, and cannot be used at all. Dead, /// This local is alive but not yet initialized. It can be written to @@ -186,6 +186,18 @@ pub enum LocalValue { Live(Operand), } +impl std::fmt::Debug for LocalValue { + // Miri debug-prints these + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use LocalValue::*; + match self { + Dead => f.debug_tuple("Dead").finish(), + Uninitialized => f.debug_tuple("Uninitialized").finish(), + Live(o) => f.debug_tuple("Live").field(o).finish(), + } + } +} + impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { /// Read the local's value or error if the local is not yet live or not live anymore. /// @@ -406,20 +418,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn force_ptr( - &self, - scalar: Scalar, - ) -> InterpResult<'tcx, Pointer> { - self.memory.force_ptr(scalar) - } - - #[inline(always)] - pub fn force_bits( - &self, - scalar: Scalar, - size: Size, - ) -> InterpResult<'tcx, u128> { - self.memory.force_bits(scalar, size) + pub fn scalar_to_ptr(&self, scalar: Scalar) -> Pointer> { + self.memory.scalar_to_ptr(scalar) } /// Call this to turn untagged "global" pointers (obtained via `tcx`) into @@ -650,7 +650,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(Some((size, align))) } ty::Dynamic(..) => { - let vtable = metadata.unwrap_meta(); + let vtable = self.scalar_to_ptr(metadata.unwrap_meta()); // Read size and align from vtable (already checks size). Ok(Some(self.read_size_and_align_from_vtable(vtable)?)) } @@ -898,8 +898,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty // due to the local having ZST type. - let ptr = ptr.assert_ptr(); - trace!("deallocating local: {:?}", self.memory.dump_alloc(ptr.alloc_id)); + trace!( + "deallocating local {:?}: {:?}", + local, + self.memory.dump_alloc(ptr.provenance.unwrap().erase_for_fmt()) + ); self.memory.deallocate(ptr, None, MemoryKind::Stack)?; }; Ok(()) @@ -975,46 +978,45 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug match self.ecx.stack()[frame].locals[local].value { LocalValue::Dead => write!(fmt, " is dead")?, LocalValue::Uninitialized => write!(fmt, " is uninitialized")?, - LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr { - Scalar::Ptr(ptr) => { - write!( - fmt, - " by align({}){} ref:", - mplace.align.bytes(), - match mplace.meta { - MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), - MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(), - } - )?; - allocs.push(ptr.alloc_id); - } - ptr => write!(fmt, " by integral ref: {:?}", ptr)?, - }, + LocalValue::Live(Operand::Indirect(mplace)) => { + write!( + fmt, + " by align({}){} ref {:?}:", + mplace.align.bytes(), + match mplace.meta { + MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), + MemPlaceMeta::Poison | MemPlaceMeta::None => String::new(), + }, + mplace.ptr, + )?; + allocs.extend(mplace.ptr.map_erase_for_fmt().provenance); + } LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { write!(fmt, " {:?}", val)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val { - allocs.push(ptr.alloc_id); + allocs.push(ptr.provenance.erase_for_fmt()); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { write!(fmt, " ({:?}, {:?})", val1, val2)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val1 { - allocs.push(ptr.alloc_id); + allocs.push(ptr.provenance.erase_for_fmt()); } if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val2 { - allocs.push(ptr.alloc_id); + allocs.push(ptr.provenance.erase_for_fmt()); } } } write!(fmt, ": {:?}", self.ecx.memory.dump_allocs(allocs)) } - Place::Ptr(mplace) => match mplace.ptr { - Scalar::Ptr(ptr) => write!( + Place::Ptr(mplace) => match mplace.ptr.map_erase_for_fmt().provenance { + Some(alloc_id) => write!( fmt, - "by align({}) ref: {:?}", + "by align({}) ref {:?}: {:?}", mplace.align.bytes(), - self.ecx.memory.dump_alloc(ptr.alloc_id) + mplace.ptr, + self.ecx.memory.dump_alloc(alloc_id) ), ptr => write!(fmt, " integral by ref: {:?}", ptr), }, diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index 2862670dc7c46..b0606aba4164b 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -20,18 +20,17 @@ use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; -use rustc_target::abi::Size; use rustc_ast::Mutability; -use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, Scalar, ValueVisitor}; +use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, ValueVisitor}; use crate::const_eval; pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< 'mir, 'tcx, MemoryKind = T, - PointerTag = (), + PointerTag = AllocId, ExtraFnVal = !, FrameExtra = (), AllocExtra = (), @@ -136,7 +135,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval: }; // link the alloc id to the actual allocation let alloc = tcx.intern_const_alloc(alloc); - leftover_allocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc)); + leftover_allocations.extend(alloc.relocations().iter().map(|&(_, alloc_id)| alloc_id)); tcx.set_alloc_id_memory(alloc_id, alloc); None } @@ -203,10 +202,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() { + let ptr = self.ecx.scalar_to_ptr(mplace.meta.unwrap_meta()); + if let Some(alloc_id) = ptr.provenance { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. - self.intern_shallow(vtable.alloc_id, InternMode::Const, None); + self.intern_shallow(alloc_id, InternMode::Const, None); } else { // Validation will error (with a better message) on an invalid vtable pointer. // Let validation show the error message, but make sure it *does* error. @@ -216,7 +216,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } // Check if we have encountered this pointer+layout combination before. // Only recurse for allocation-backed pointers. - if let Scalar::Ptr(ptr) = mplace.ptr { + if let Some(alloc_id) = mplace.ptr.provenance { // Compute the mode with which we intern this. Our goal here is to make as many // statics as we can immutable so they can be placed in read-only memory by LLVM. let ref_mode = match self.mode { @@ -259,7 +259,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory InternMode::Const } }; - match self.intern_shallow(ptr.alloc_id, ref_mode, Some(referenced_ty)) { + match self.intern_shallow(alloc_id, ref_mode, Some(referenced_ty)) { // No need to recurse, these are interned already and statics may have // cycles, so we don't want to recurse there Some(IsStaticOrFn) => {} @@ -321,7 +321,7 @@ where leftover_allocations, // The outermost allocation must exist, because we allocated it with // `Memory::allocate`. - ret.ptr.assert_ptr().alloc_id, + ret.ptr.provenance.unwrap(), base_intern_mode, Some(ret.layout.ty), ); @@ -395,9 +395,9 @@ where } let alloc = tcx.intern_const_alloc(alloc); tcx.set_alloc_id_memory(alloc_id, alloc); - for &(_, ((), reloc)) in alloc.relocations().iter() { - if leftover_allocations.insert(reloc) { - todo.push(reloc); + for &(_, alloc_id) in alloc.relocations().iter() { + if leftover_allocations.insert(alloc_id) { + todo.push(alloc_id); } } } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { @@ -430,9 +430,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> ) -> InterpResult<'tcx, &'tcx Allocation> { let dest = self.allocate(layout, MemoryKind::Stack)?; f(self, &dest)?; - let ptr = dest.ptr.assert_ptr(); - assert_eq!(ptr.offset, Size::ZERO); - let mut alloc = self.memory.alloc_map.remove(&ptr.alloc_id).unwrap().1; + let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1; alloc.mutability = Mutability::Not; Ok(self.tcx.intern_const_alloc(alloc)) } diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index ad9cf3e7d2fe9..88dd94802d14f 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -18,6 +18,7 @@ use rustc_target::abi::{Abi, Align, LayoutOf as _, Primitive, Size}; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, + Pointer, }; mod caller_location; @@ -138,7 +139,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::caller_location => { let span = self.find_closest_untracked_caller_location(); let location = self.alloc_caller_location_for_span(span); - self.write_scalar(location.ptr, dest)?; + self.write_immediate(location.to_ref(self), dest)?; } sym::min_align_of_val | sym::size_of_val => { @@ -190,7 +191,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ty = substs.type_at(0); let layout_of = self.layout_of(ty)?; let val = self.read_scalar(&args[0])?.check_init()?; - let bits = self.force_bits(val, layout_of.size)?; + let bits = val.to_bits(layout_of.size)?; let kind = match layout_of.abi { Abi::Scalar(ref scalar) => scalar.value, _ => span_bug!( @@ -238,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // term since the sign of the second term can be inferred from this and // the fact that the operation has overflowed (if either is 0 no // overflow can occur) - let first_term: u128 = self.force_bits(l.to_scalar()?, l.layout.size)?; + let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?; let first_term_positive = first_term & (1 << (num_bits - 1)) == 0; if first_term_positive { // Negative overflow not possible since the positive first term @@ -298,7 +299,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?; if overflowed { let layout = self.layout_of(substs.type_at(0))?; - let r_val = self.force_bits(r.to_scalar()?, layout.size)?; + let r_val = r.to_scalar()?.to_bits(layout.size)?; if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); } else { @@ -312,9 +313,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) let layout = self.layout_of(substs.type_at(0))?; let val = self.read_scalar(&args[0])?.check_init()?; - let val_bits = self.force_bits(val, layout.size)?; + let val_bits = val.to_bits(layout.size)?; let raw_shift = self.read_scalar(&args[1])?.check_init()?; - let raw_shift_bits = self.force_bits(raw_shift, layout.size)?; + let raw_shift_bits = raw_shift.to_bits(layout.size)?; let width_bits = u128::from(layout.size.bits()); let shift_bits = raw_shift_bits % width_bits; let inv_shift_bits = (width_bits - shift_bits) % width_bits; @@ -331,12 +332,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.copy_intrinsic(&args[0], &args[1], &args[2], /*nonoverlapping*/ false)?; } sym::offset => { - let ptr = self.read_scalar(&args[0])?.check_init()?; + let ptr = self.read_pointer(&args[0])?; let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?; let pointee_ty = substs.type_at(0); let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; - self.write_scalar(offset_ptr, dest)?; + self.write_scalar(Scalar::from_maybe_pointer(offset_ptr, self), dest)?; } sym::arith_offset => { let ptr = self.read_scalar(&args[0])?.check_init()?; @@ -376,9 +377,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if !done { // General case: we need two pointers. - let a = self.force_ptr(a)?; - let b = self.force_ptr(b)?; - if a.alloc_id != b.alloc_id { + let a = self.scalar_to_ptr(a); + let b = self.scalar_to_ptr(b); + let (a_alloc_id, a_offset, _) = self.memory.ptr_force_alloc(a)?; + let (b_alloc_id, b_offset, _) = self.memory.ptr_force_alloc(b)?; + if a_alloc_id != b_alloc_id { throw_ub_format!( "ptr_offset_from cannot compute offset of pointers into different \ allocations.", @@ -386,8 +389,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } let usize_layout = self.layout_of(self.tcx.types.usize)?; let isize_layout = self.layout_of(self.tcx.types.isize)?; - let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); - let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); + let a_offset = ImmTy::from_uint(a_offset.bytes(), usize_layout); + let b_offset = ImmTy::from_uint(b_offset.bytes(), usize_layout); let (val, _overflowed, _ty) = self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?; let pointee_layout = self.layout_of(substs.type_at(0))?; @@ -513,10 +516,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value. pub fn ptr_offset_inbounds( &self, - ptr: Scalar, + ptr: Pointer>, pointee_ty: Ty<'tcx>, offset_count: i64, - ) -> InterpResult<'tcx, Scalar> { + ) -> InterpResult<'tcx, Pointer>> { // We cannot overflow i64 as a type's size must be <= isize::MAX. let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); // The computed offset, in bytes, cannot overflow an isize. @@ -524,7 +527,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; // The offset being in bounds cannot rely on "wrapping around" the address space. // So, first rule out overflows in the pointer arithmetic. - let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; + let offset_ptr = ptr.signed_offset(offset_bytes, self)?; // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the // memory between these pointers must be accessible. Note that we do not require the // pointers to be properly aligned (unlike a read/write operation). @@ -558,8 +561,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) })?; - let src = self.read_scalar(&src)?.check_init()?; - let dst = self.read_scalar(&dst)?.check_init()?; + let src = self.read_pointer(&src)?; + let dst = self.read_pointer(&dst)?; self.memory.copy(src, align, dst, align, size, nonoverlapping) } @@ -572,8 +575,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?; assert!(!layout.is_unsized()); - let lhs = self.read_scalar(lhs)?.check_init()?; - let rhs = self.read_scalar(rhs)?.check_init()?; + let lhs = self.read_pointer(lhs)?; + let rhs = self.read_pointer(rhs)?; let lhs_bytes = self.memory.read_bytes(lhs, layout.size)?; let rhs_bytes = self.memory.read_bytes(rhs, layout.size)?; Ok(Scalar::from_bool(lhs_bytes == rhs_bytes)) diff --git a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs index 4a3278030b5d9..022129b2a2204 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs @@ -96,7 +96,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); // Initialize fields. - self.write_immediate(file.to_ref(), &self.mplace_field(&location, 0).unwrap().into()) + self.write_immediate(file.to_ref(self), &self.mplace_field(&location, 0).unwrap().into()) .expect("writing to memory we just allocated cannot fail"); self.write_scalar(line, &self.mplace_field(&location, 1).unwrap().into()) .expect("writing to memory we just allocated cannot fail"); diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index 5b8c0788cbc84..a9e1c605a1f91 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -13,8 +13,8 @@ use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use super::{ - AllocId, Allocation, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, LocalValue, - MemPlace, Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar, StackPopUnwind, + AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, LocalValue, MemPlace, Memory, + MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, }; /// Data returned by Machine::stack_pop, @@ -84,12 +84,8 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static; - /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" - /// . - /// The `default()` is used for pointers to consts, statics, vtables and functions. - /// The `Debug` formatting is used for displaying pointers; we cannot use `Display` - /// as `()` does not implement that, but it should be "nice" output. - type PointerTag: Debug + Copy + Eq + Hash + 'static; + /// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to. + type PointerTag: Provenance + Eq + Hash + 'static; /// Machines can define extra (non-instance) things that represent values of function pointers. /// For example, Miri uses this to return a function pointer from `dlsym` @@ -287,7 +283,10 @@ pub trait Machine<'mir, 'tcx>: Sized { /// this will return an unusable tag (i.e., accesses will be UB)! /// /// Called on the id returned by `thread_local_static_alloc_id` and `extern_static_alloc_id`, if needed. - fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag; + fn tag_global_base_pointer( + memory_extra: &Self::MemoryExtra, + ptr: Pointer, + ) -> Pointer; /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is @@ -400,31 +399,24 @@ pub trait Machine<'mir, 'tcx>: Sized { Ok(StackPopJump::Normal) } - fn int_to_ptr( - _mem: &Memory<'mir, 'tcx, Self>, - int: u64, - ) -> InterpResult<'tcx, Pointer> { - Err((if int == 0 { - // This is UB, seriously. - // (`DanglingIntPointer` with these exact arguments has special printing code.) - err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest)) - } else { - // This is just something we cannot support during const-eval. - err_unsup!(ReadBytesAsPointer) - }) - .into()) - } + /// "Int-to-pointer cast" + fn ptr_from_addr( + mem: &Memory<'mir, 'tcx, Self>, + addr: u64, + ) -> Pointer>; - fn ptr_to_int( - _mem: &Memory<'mir, 'tcx, Self>, - _ptr: Pointer, - ) -> InterpResult<'tcx, u64>; + /// Convert a pointer with provenance into an allocation-offset pair, + /// or a `None` with an absolute address if that conversion is not possible. + fn ptr_get_alloc( + mem: &Memory<'mir, 'tcx, Self>, + ptr: Pointer, + ) -> (Option, Size); } // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines // (CTFE and ConstProp) use the same instance. Here, we share that code. pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { - type PointerTag = (); + type PointerTag = AllocId; type ExtraFnVal = !; type MemoryMap = @@ -467,19 +459,33 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn init_allocation_extra<'b>( _memory_extra: &Self::MemoryExtra, - _id: AllocId, + id: AllocId, alloc: Cow<'b, Allocation>, _kind: Option>, ) -> (Cow<'b, Allocation>, Self::PointerTag) { // We do not use a tag so we can just cheaply forward the allocation - (alloc, ()) + (alloc, id) } #[inline(always)] fn tag_global_base_pointer( _memory_extra: &Self::MemoryExtra, - _id: AllocId, - ) -> Self::PointerTag { - () + ptr: Pointer, + ) -> Pointer { + ptr + } + + #[inline(always)] + fn ptr_from_addr(_mem: &Memory<$mir, $tcx, Self>, addr: u64) -> Pointer> { + Pointer::new(None, Size::from_bytes(addr)) + } + + #[inline(always)] + fn ptr_get_alloc( + _mem: &Memory<$mir, $tcx, Self>, + ptr: Pointer, + ) -> (Option, Size) { + let (alloc_id, offset) = ptr.into_parts(); + (Some(alloc_id), offset) } } diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 5f719cc160706..6a1d1bc3b2972 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::collections::VecDeque; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::fmt; use std::ptr; @@ -19,7 +19,8 @@ use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use super::{ alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, - InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Scalar, ScalarMaybeUninit, + InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar, + ScalarMaybeUninit, }; use crate::util::pretty; @@ -162,25 +163,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { #[inline] pub fn global_base_pointer( &self, - mut ptr: Pointer, + ptr: Pointer, ) -> InterpResult<'tcx, Pointer> { + let (alloc_id, offset) = ptr.into_parts(); // We need to handle `extern static`. - let ptr = match self.tcx.get_global_alloc(ptr.alloc_id) { + let alloc_id = match self.tcx.get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => { bug!("global memory cannot point to thread-local static") } Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => { - ptr.alloc_id = M::extern_static_alloc_id(self, def_id)?; - ptr + M::extern_static_alloc_id(self, def_id)? } _ => { // No need to change the `AllocId`. - ptr + alloc_id } }; // And we need to get the tag. - let tag = M::tag_global_base_pointer(&self.extra, ptr.alloc_id); - Ok(ptr.with_tag(tag)) + Ok(M::tag_global_base_pointer(&self.extra, Pointer::new(alloc_id, offset))) } pub fn create_fn_alloc( @@ -237,18 +237,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // This is a new allocation, not a new global one, so no `global_base_ptr`. let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); - Pointer::from(id).with_tag(tag) + Pointer::new(tag, Size::ZERO) } pub fn reallocate( &mut self, - ptr: Pointer, + ptr: Pointer>, old_size_and_align: Option<(Size, Align)>, new_size: Size, new_align: Align, kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { - if ptr.offset.bytes() != 0 { + let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + if offset.bytes() != 0 { throw_ub_format!( "reallocating {:?} which does not point to the beginning of an object", ptr @@ -260,7 +261,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let new_ptr = self.allocate(new_size, new_align, kind)?; let old_size = match old_size_and_align { Some((size, _align)) => size, - None => self.get_raw(ptr.alloc_id)?.size(), + None => self.get_raw(alloc_id)?.size(), }; // This will also call the access hooks. self.copy( @@ -271,50 +272,51 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { old_size.min(new_size), /*nonoverlapping*/ true, )?; - self.deallocate(ptr, old_size_and_align, kind)?; + self.deallocate(ptr.into(), old_size_and_align, kind)?; Ok(new_ptr) } pub fn deallocate( &mut self, - ptr: Pointer, + ptr: Pointer>, old_size_and_align: Option<(Size, Align)>, kind: MemoryKind, ) -> InterpResult<'tcx> { - trace!("deallocating: {}", ptr.alloc_id); + let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + trace!("deallocating: {}", alloc_id); - if ptr.offset.bytes() != 0 { + if offset.bytes() != 0 { throw_ub_format!( "deallocating {:?} which does not point to the beginning of an object", ptr ); } - let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { + let (alloc_kind, mut alloc) = match self.alloc_map.remove(&alloc_id) { Some(alloc) => alloc, None => { // Deallocating global memory -- always an error - return Err(match self.tcx.get_global_alloc(ptr.alloc_id) { + return Err(match self.tcx.get_global_alloc(alloc_id) { Some(GlobalAlloc::Function(..)) => { - err_ub_format!("deallocating {}, which is a function", ptr.alloc_id) + err_ub_format!("deallocating {}, which is a function", alloc_id) } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { - err_ub_format!("deallocating {}, which is static memory", ptr.alloc_id) + err_ub_format!("deallocating {}, which is static memory", alloc_id) } - None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), + None => err_ub!(PointerUseAfterFree(alloc_id)), } .into()); } }; if alloc.mutability == Mutability::Not { - throw_ub_format!("deallocating immutable allocation {}", ptr.alloc_id); + throw_ub_format!("deallocating immutable allocation {}", alloc_id); } if alloc_kind != kind { throw_ub_format!( "deallocating {}, which is {} memory, using {} deallocation operation", - ptr.alloc_id, + alloc_id, alloc_kind, kind ); @@ -323,7 +325,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if size != alloc.size() || align != alloc.align { throw_ub_format!( "incorrect layout on deallocation: {} has size {} and alignment {}, but gave size {} and alignment {}", - ptr.alloc_id, + alloc_id, alloc.size().bytes(), alloc.align.bytes(), size.bytes(), @@ -337,7 +339,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { M::memory_deallocated(&mut self.extra, &mut alloc.extra, ptr, size)?; // Don't forget to remember size and align of this now-dead allocation - let old = self.dead_alloc_map.insert(ptr.alloc_id, (size, alloc.align)); + let old = self.dead_alloc_map.insert(alloc_id, (size, alloc.align)); if old.is_some() { bug!("Nothing can be deallocated twice"); } @@ -345,52 +347,61 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(()) } - /// Internal helper function for APIs that offer memory access based on `Scalar` pointers. + /// Internal helper function to determine the allocation and offset of a pointer (if any). #[inline(always)] - pub(super) fn check_ptr_access( + fn get_ptr_access( &self, - sptr: Scalar, + ptr: Pointer>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option>> { + ) -> InterpResult<'tcx, Option<(AllocId, Size, Pointer)>> { let align = M::enforce_alignment(&self.extra).then_some(align); - self.check_and_deref_ptr(sptr, size, align, CheckInAllocMsg::MemoryAccessTest, |ptr| { - let (size, align) = - self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferenceable)?; - Ok((size, align, ptr)) - }) + self.check_and_deref_ptr( + ptr, + size, + align, + CheckInAllocMsg::MemoryAccessTest, + |alloc_id, offset, ptr| { + let (size, align) = + self.get_size_and_align(alloc_id, AllocCheck::Dereferenceable)?; + Ok((size, align, (alloc_id, offset, ptr))) + }, + ) } - /// Check if the given scalar is allowed to do a memory access of given `size` and `align` + /// Check if the given pointer is allowed to do a memory access of given `size` and `align` /// (ignoring `M::enforce_alignment`). The caller can control the error message for the /// out-of-bounds case. #[inline(always)] pub fn check_ptr_access_align( &self, - sptr: Scalar, + ptr: Pointer>, size: Size, align: Align, msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { - self.check_and_deref_ptr(sptr, size, Some(align), msg, |ptr| { - let (size, align) = - self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferenceable)?; + self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| { + let (size, align) = self.get_size_and_align(alloc_id, AllocCheck::Dereferenceable)?; Ok((size, align, ())) })?; Ok(()) } /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference - /// to the allocation it points to. Supports both shared and mutable references, to the actual + /// to the allocation it points to. Supports both shared and mutable references, as the actual /// checking is offloaded to a helper closure. `align` defines whether and which alignment check /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned. fn check_and_deref_ptr( &self, - sptr: Scalar, + ptr: Pointer>, size: Size, align: Option, msg: CheckInAllocMsg, - alloc_size: impl FnOnce(Pointer) -> InterpResult<'tcx, (Size, Align, T)>, + alloc_size: impl FnOnce( + AllocId, + Size, + Pointer, + ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option> { fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> { if offset % align.bytes() == 0 { @@ -405,53 +416,50 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } } - // Normalize to a `Pointer` if we definitely need one. - let normalized = if size.bytes() == 0 { - // Can be an integer, just take what we got. We do NOT `force_bits` here; - // if this is already a `Pointer` we want to do the bounds checks! - sptr + // Extract from the pointer an `Option` and an offset, which is relative to the + // allocation or (if that is `None`) an absolute address. + let ptr_or_addr = if size.bytes() == 0 { + // Let's see what we can do, but don't throw errors if there's nothing there. + self.ptr_try_get_alloc(ptr) } else { - // A "real" access, we must get a pointer to be able to check the bounds. - Scalar::from(self.force_ptr(sptr)?) + // A "real" access, we insist on getting an `AllocId`. + Ok(self.ptr_force_alloc(ptr)?) }; - Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) { - Ok(bits) => { - let bits = u64::try_from(bits).unwrap(); // it's ptr-sized - assert!(size.bytes() == 0); + Ok(match ptr_or_addr { + Err(addr) => { + // No memory is actually being accessed. + debug_assert!(size.bytes() == 0); // Must be non-null. - if bits == 0 { + if addr == 0 { throw_ub!(DanglingIntPointer(0, msg)) } // Must be aligned. if let Some(align) = align { - check_offset_align(bits, align)?; + check_offset_align(addr, align)?; } None } - Err(ptr) => { - let (allocation_size, alloc_align, ret_val) = alloc_size(ptr)?; + Ok((alloc_id, offset, ptr)) => { + let (allocation_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, ptr)?; // Test bounds. This also ensures non-null. - // It is sufficient to check this for the end pointer. The addition - // checks for overflow. - let end_ptr = ptr.offset(size, self)?; - if end_ptr.offset > allocation_size { - // equal is okay! - throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size }) + // It is sufficient to check this for the end pointer. Also check for overflow! + if offset.checked_add(size, &self.tcx).map_or(true, |end| end > allocation_size) { + throw_ub!(PointerOutOfBounds { alloc_id, offset, size, allocation_size, msg }) } // Test align. Check this last; if both bounds and alignment are violated // we want the error to be about the bounds. if let Some(align) = align { if M::force_int_for_alignment_check(&self.extra) { - let bits = self - .force_bits(ptr.into(), self.pointer_size()) + let addr = Scalar::from(ptr) + .to_machine_usize(&self.tcx) .expect("ptr-to-int cast for align check should never fail"); - check_offset_align(bits.try_into().unwrap(), align)?; + check_offset_align(addr, align)?; } else { // Check allocation alignment and offset alignment. if alloc_align.bytes() < align.bytes() { throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); } - check_offset_align(ptr.offset.bytes(), align)?; + check_offset_align(offset.bytes(), align)?; } } @@ -463,13 +471,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } /// Test if the pointer might be null. - pub fn ptr_may_be_null(&self, ptr: Pointer) -> bool { - let (size, _align) = self - .get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead) - .expect("alloc info with MaybeDead cannot fail"); - // If the pointer is out-of-bounds, it may be null. - // Note that one-past-the-end (offset == size) is still inbounds, and never null. - ptr.offset > size + pub fn ptr_may_be_null(&self, ptr: Pointer>) -> bool { + match self.ptr_try_get_alloc(ptr) { + Ok((alloc_id, offset, _)) => { + let (size, _align) = self + .get_size_and_align(alloc_id, AllocCheck::MaybeDead) + .expect("alloc info with MaybeDead cannot fail"); + // If the pointer is out-of-bounds, it may be null. + // Note that one-past-the-end (offset == size) is still inbounds, and never null. + offset > size + } + Err(offset) => offset == 0, + } } } @@ -522,8 +535,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { alloc, M::GLOBAL_KIND.map(MemoryKind::Machine), ); - // Sanity check that this is the same pointer we would have gotten via `global_base_pointer`. - debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id)); + // Sanity check that this is the same tag we would have gotten via `global_base_pointer`. + debug_assert!(tag == M::tag_global_base_pointer(memory_extra, id.into()).provenance); Ok(alloc) } @@ -566,30 +579,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get<'a>( &'a self, - sptr: Scalar, + ptr: Pointer>, size: Size, align: Align, ) -> InterpResult<'tcx, Option>> { let align = M::enforce_alignment(&self.extra).then_some(align); let ptr_and_alloc = self.check_and_deref_ptr( - sptr, + ptr, size, align, CheckInAllocMsg::MemoryAccessTest, - |ptr| { - let alloc = self.get_raw(ptr.alloc_id)?; - Ok((alloc.size(), alloc.align, (ptr, alloc))) + |alloc_id, offset, ptr| { + let alloc = self.get_raw(alloc_id)?; + Ok((alloc.size(), alloc.align, (alloc_id, offset, ptr, alloc))) }, )?; - if let Some((ptr, alloc)) = ptr_and_alloc { + if let Some((alloc_id, offset, ptr, alloc)) = ptr_and_alloc { M::memory_read(&self.extra, &alloc.extra, ptr, size)?; - let range = alloc_range(ptr.offset, size); - Ok(Some(AllocRef { alloc, range, tcx: self.tcx, alloc_id: ptr.alloc_id })) + let range = alloc_range(offset, size); + Ok(Some(AllocRef { alloc, range, tcx: self.tcx, alloc_id })) } else { // Even in this branch we have to be sure that we actually access the allocation, in // order to ensure that `static FOO: Type = FOO;` causes a cycle error instead of // magically pulling *any* ZST value from the ether. However, the `get_raw` above is - // always called when `sptr` is truly a `Pointer`, so we are good. + // always called when `ptr` has an `AllocId`. Ok(None) } } @@ -638,19 +651,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// "Safe" (bounds and align-checked) allocation access. pub fn get_mut<'a>( &'a mut self, - sptr: Scalar, + ptr: Pointer>, size: Size, align: Align, ) -> InterpResult<'tcx, Option>> { - let ptr = self.check_ptr_access(sptr, size, align)?; - if let Some(ptr) = ptr { + let parts = self.get_ptr_access(ptr, size, align)?; + if let Some((alloc_id, offset, ptr)) = parts { let tcx = self.tcx; // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. - let (alloc, extra) = self.get_raw_mut(ptr.alloc_id)?; + let (alloc, extra) = self.get_raw_mut(alloc_id)?; M::memory_written(extra, &mut alloc.extra, ptr, size)?; - let range = alloc_range(ptr.offset, size); - Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id: ptr.alloc_id })) + let range = alloc_range(offset, size); + Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id })) } else { Ok(None) } @@ -740,14 +753,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn get_fn( &self, - ptr: Scalar, + ptr: Pointer>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value. - if ptr.offset.bytes() != 0 { - throw_ub!(InvalidFunctionPointer(ptr.erase_tag())) + let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + if offset.bytes() != 0 { + throw_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())) } - self.get_fn_alloc(ptr.alloc_id) - .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) + self.get_fn_alloc(alloc_id) + .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -786,7 +799,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if reachable.insert(id) { // This is a new allocation, add its relocations to `todo`. if let Some((_, alloc)) = self.alloc_map.get(id) { - todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id)); + todo.extend(alloc.relocations().values().map(|tag| tag.erase_for_fmt())); } } } @@ -820,14 +833,14 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Cannot be a closure because it is generic in `Tag`, `Extra`. - fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>( + fn write_allocation_track_relocs<'tcx, Tag: Provenance, Extra>( fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'tcx>, allocs_to_print: &mut VecDeque, alloc: &Allocation, ) -> std::fmt::Result { - for &(_, target_id) in alloc.relocations().values() { - allocs_to_print.push_back(target_id); + for alloc_id in alloc.relocations().values().map(|tag| tag.erase_for_fmt()) { + allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", pretty::display_allocation(tcx, alloc)) } @@ -930,8 +943,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Reads the given number of bytes from memory. Returns them as a slice. /// /// Performs appropriate bounds checks. - pub fn read_bytes(&self, sptr: Scalar, size: Size) -> InterpResult<'tcx, &[u8]> { - let alloc_ref = match self.get(sptr, size, Align::ONE)? { + pub fn read_bytes( + &self, + ptr: Pointer>, + size: Size, + ) -> InterpResult<'tcx, &[u8]> { + let alloc_ref = match self.get(ptr, size, Align::ONE)? { Some(a) => a, None => return Ok(&[]), // zero-sized access }; @@ -948,7 +965,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Performs appropriate bounds checks. pub fn write_bytes( &mut self, - sptr: Scalar, + ptr: Pointer>, src: impl IntoIterator, ) -> InterpResult<'tcx> { let mut src = src.into_iter(); @@ -957,7 +974,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { assert_eq!(lower, len, "can only write iterators with a precise length"); let size = Size::from_bytes(len); - let alloc_ref = match self.get_mut(sptr, size, Align::ONE)? { + let alloc_ref = match self.get_mut(ptr, size, Align::ONE)? { Some(alloc_ref) => alloc_ref, None => { // zero-sized access @@ -984,9 +1001,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn copy( &mut self, - src: Scalar, + src: Pointer>, src_align: Align, - dest: Scalar, + dest: Pointer>, dest_align: Align, size: Size, nonoverlapping: bool, @@ -996,9 +1013,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn copy_repeatedly( &mut self, - src: Scalar, + src: Pointer>, src_align: Align, - dest: Scalar, + dest: Pointer>, dest_align: Align, size: Size, num_copies: u64, @@ -1006,22 +1023,22 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { let tcx = self.tcx; // We need to do our own bounds-checks. - let src = self.check_ptr_access(src, size, src_align)?; - let dest = self.check_ptr_access(dest, size * num_copies, dest_align)?; // `Size` multiplication + let src_parts = self.get_ptr_access(src, size, src_align)?; + let dest_parts = self.get_ptr_access(dest, size * num_copies, dest_align)?; // `Size` multiplication // FIXME: we look up both allocations twice here, once ebfore for the `check_ptr_access` // and once below to get the underlying `&[mut] Allocation`. // Source alloc preparations and access hooks. - let src = match src { + let (src_alloc_id, src_offset, src) = match src_parts { None => return Ok(()), // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do. Some(src_ptr) => src_ptr, }; - let src_alloc = self.get_raw(src.alloc_id)?; + let src_alloc = self.get_raw(src_alloc_id)?; M::memory_read(&self.extra, &src_alloc.extra, src, size)?; // We need the `dest` ptr for the next operation, so we get it now. // We already did the source checks and called the hooks so we are good to return early. - let dest = match dest { + let (dest_alloc_id, dest_offset, dest) = match dest_parts { None => return Ok(()), // Zero-sized *destiantion*. Some(dest_ptr) => dest_ptr, }; @@ -1033,23 +1050,23 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // relocations overlapping the edges; those would not be handled correctly). let relocations = src_alloc.prepare_relocation_copy( self, - alloc_range(src.offset, size), - dest.offset, + alloc_range(src_offset, size), + dest_offset, num_copies, ); // Prepare a copy of the initialization mask. - let compressed = src_alloc.compress_uninit_range(alloc_range(src.offset, size)); + let compressed = src_alloc.compress_uninit_range(alloc_range(src_offset, size)); // This checks relocation edges on the src. let src_bytes = src_alloc - .get_bytes_with_uninit_and_ptr(&tcx, alloc_range(src.offset, size)) - .map_err(|e| e.to_interp_error(src.alloc_id))? + .get_bytes_with_uninit_and_ptr(&tcx, alloc_range(src_offset, size)) + .map_err(|e| e.to_interp_error(src_alloc_id))? .as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation // Destination alloc preparations and access hooks. - let (dest_alloc, extra) = self.get_raw_mut(dest.alloc_id)?; + let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?; M::memory_written(extra, &mut dest_alloc.extra, dest, size * num_copies)?; let dest_bytes = dest_alloc - .get_bytes_mut_ptr(&tcx, alloc_range(dest.offset, size * num_copies)) + .get_bytes_mut_ptr(&tcx, alloc_range(dest_offset, size * num_copies)) .as_mut_ptr(); if compressed.no_bytes_init() { @@ -1059,7 +1076,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // This also avoids writing to the target bytes so that the backing allocation is never // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. - dest_alloc.mark_init(alloc_range(dest.offset, size * num_copies), false); // `Size` multiplication + dest_alloc.mark_init(alloc_range(dest_offset, size * num_copies), false); // `Size` multiplication dest_alloc.mark_relocation_range(relocations); return Ok(()); } @@ -1070,11 +1087,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // The pointers above remain valid even if the `HashMap` table is moved around because they // point into the `Vec` storing the bytes. unsafe { - if src.alloc_id == dest.alloc_id { + if src_alloc_id == dest_alloc_id { if nonoverlapping { // `Size` additions - if (src.offset <= dest.offset && src.offset + size > dest.offset) - || (dest.offset <= src.offset && dest.offset + size > src.offset) + if (src_offset <= dest_offset && src_offset + size > dest_offset) + || (dest_offset <= src_offset && dest_offset + size > src_offset) { throw_ub_format!("copy_nonoverlapping called on overlapping ranges") } @@ -1101,7 +1118,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // now fill in all the "init" data dest_alloc.mark_compressed_init_range( &compressed, - alloc_range(dest.offset, size), + alloc_range(dest_offset, size), num_copies, ); // copy the relocations to the destination @@ -1113,24 +1130,44 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { - pub fn force_ptr( - &self, - scalar: Scalar, - ) -> InterpResult<'tcx, Pointer> { - match scalar { - Scalar::Ptr(ptr) => Ok(ptr), - _ => M::int_to_ptr(&self, scalar.to_machine_usize(self)?), + pub fn scalar_to_ptr(&self, scalar: Scalar) -> Pointer> { + match scalar.to_bits_or_ptr(self.pointer_size(), &self.tcx) { + Err(ptr) => ptr.into(), + Ok(bits) => { + let addr = u64::try_from(bits).unwrap(); + M::ptr_from_addr(&self, addr) + } } } - pub fn force_bits( + /// Internal helper for turning a "maybe pointer" into a proper pointer (and some information + /// about where it points), or an absolute address. + pub(super) fn ptr_try_get_alloc( &self, - scalar: Scalar, - size: Size, - ) -> InterpResult<'tcx, u128> { - match scalar.to_bits_or_ptr(size, self) { - Ok(bits) => Ok(bits), - Err(ptr) => Ok(M::ptr_to_int(&self, ptr)?.into()), + ptr: Pointer>, + ) -> Result<(AllocId, Size, Pointer), u64> { + match ptr.into_pointer_or_offset() { + Ok(ptr) => { + let (alloc_id, offset) = M::ptr_get_alloc(self, ptr); + if let Some(alloc_id) = alloc_id { + Ok((alloc_id, offset, ptr)) + } else { + Err(offset.bytes()) + } + } + Err(offset) => Err(offset.bytes()), } } + + /// Internal helper for turning a "maybe pointer" into a proper pointer (and some information + /// about where it points). + #[inline(always)] + pub(super) fn ptr_force_alloc( + &self, + ptr: Pointer>, + ) -> InterpResult<'tcx, (AllocId, Size, Pointer)> { + self.ptr_try_get_alloc(ptr).map_err(|offset| { + err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into() + }) + } } diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index 06432a8b9024b..db054d1f279cd 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -15,8 +15,9 @@ use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; use super::{ - alloc_range, from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx, - InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, + alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, GlobalId, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Provenance, + Scalar, ScalarMaybeUninit, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -26,14 +27,24 @@ use super::{ /// operations and wide pointers. This idea was taken from rustc's codegen. /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. -#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Immediate { +#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash)] +pub enum Immediate { Scalar(ScalarMaybeUninit), ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Immediate, 56); +//FIXME rustc_data_structures::static_assert_size!(Immediate, 56); + +impl std::fmt::Debug for Immediate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Immediate::*; + match self { + Scalar(s) => f.debug_tuple("Scalar").field(s).finish(), + ScalarPair(s1, s2) => f.debug_tuple("ScalarPair").field(s1).field(s2).finish(), + } + } +} impl From> for Immediate { #[inline(always)] @@ -81,26 +92,33 @@ impl<'tcx, Tag> Immediate { // ScalarPair needs a type to interpret, so we often have an immediate and a type together // as input for binary and cast operations. -#[derive(Copy, Clone, Debug)] -pub struct ImmTy<'tcx, Tag = ()> { +#[derive(Copy, Clone)] +pub struct ImmTy<'tcx, Tag = AllocId> { imm: Immediate, pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); +//FIXME rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); + +impl<'tcx, Tag: Provenance> std::fmt::Debug for ImmTy<'tcx, Tag> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ImmTy { imm, layout } = self; + f.debug_struct("ImmTy").field("imm", imm).field("layout", layout).finish() + } +} -impl std::fmt::Display for ImmTy<'tcx, Tag> { +impl std::fmt::Display for ImmTy<'tcx, Tag> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Helper function for printing a scalar to a FmtPrinter - fn p<'a, 'tcx, F: std::fmt::Write, Tag>( + fn p<'a, 'tcx, F: std::fmt::Write, Tag: Provenance>( cx: FmtPrinter<'a, 'tcx, F>, s: ScalarMaybeUninit, ty: Ty<'tcx>, ) -> Result, std::fmt::Error> { match s { ScalarMaybeUninit::Scalar(s) => { - cx.pretty_print_const_scalar(s.erase_tag(), ty, true) + cx.pretty_print_const_scalar(s.erase_for_fmt(), ty, true) } ScalarMaybeUninit::Uninit => cx.typed_value( |mut this| { @@ -120,11 +138,11 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { p(cx, s, ty)?; return Ok(()); } - write!(f, "{}: {}", s.erase_tag(), self.layout.ty) + write!(f, "{}: {}", s.erase_for_fmt(), self.layout.ty) } Immediate::ScalarPair(a, b) => { // FIXME(oli-obk): at least print tuples and slices nicely - write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) + write!(f, "({}, {}): {}", a.erase_for_fmt(), b.erase_for_fmt(), self.layout.ty,) } } }) @@ -142,14 +160,24 @@ impl<'tcx, Tag> std::ops::Deref for ImmTy<'tcx, Tag> { /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. -#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] -pub enum Operand { +#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash)] +pub enum Operand { Immediate(Immediate), Indirect(MemPlace), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct OpTy<'tcx, Tag = ()> { +impl std::fmt::Debug for Operand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Operand::*; + match self { + Immediate(i) => f.debug_tuple("Immediate").field(i).finish(), + Indirect(p) => f.debug_tuple("Indirect").field(p).finish(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct OpTy<'tcx, Tag = AllocId> { op: Operand, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, } @@ -157,6 +185,13 @@ pub struct OpTy<'tcx, Tag = ()> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(OpTy<'_, ()>, 80); +impl<'tcx, Tag: Provenance> std::fmt::Debug for OpTy<'tcx, Tag> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let OpTy { op, layout } = self; + f.debug_struct("OpTy").field("op", op).field("layout", layout).finish() + } +} + impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> { type Target = Operand; #[inline(always)] @@ -225,19 +260,6 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Normalize `place.ptr` to a `Pointer` if this is a place and not a ZST. - /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. - #[inline] - pub fn force_op_ptr( - &self, - op: &OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - match op.try_as_mplace(self) { - Ok(mplace) => Ok(self.force_mplace_ptr(mplace)?.into()), - Err(imm) => Ok(imm.into()), // Nothing to cast/force - } - } - /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. fn try_read_immediate_from_mplace( @@ -291,7 +313,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, src: &OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, Result, MPlaceTy<'tcx, M::PointerTag>>> { - Ok(match src.try_as_mplace(self) { + Ok(match src.try_as_mplace() { Ok(ref mplace) => { if let Some(val) = self.try_read_immediate_from_mplace(mplace)? { Ok(val) @@ -324,6 +346,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(self.read_immediate(op)?.to_scalar_or_uninit()) } + /// Read a pointer from a place. + pub fn read_pointer( + &self, + op: &OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, Pointer>> { + Ok(self.scalar_to_ptr(self.read_scalar(op)?.check_init()?)) + } + // Turn the wide MPlace into a string (must already be dereferenced!) pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; @@ -338,7 +368,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { op: &OpTy<'tcx, M::PointerTag>, field: usize, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let base = match op.try_as_mplace(self) { + let base = match op.try_as_mplace() { Ok(ref mplace) => { // We can reuse the mplace field computation logic for indirect operands. let field = self.mplace_field(mplace, field)?; @@ -381,7 +411,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.operand_field(op, index) } else { // Indexing into a big array. This must be an mplace. - let mplace = op.assert_mem_place(self); + let mplace = op.assert_mem_place(); Ok(self.mplace_index(&mplace, index)?.into()) } } @@ -392,7 +422,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { variant: VariantIdx, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { // Downcasts only change the layout - Ok(match op.try_as_mplace(self) { + Ok(match op.try_as_mplace() { Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(), Err(..) => { let layout = op.layout.for_variant(self, variant); @@ -414,7 +444,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Subslice { .. } | ConstantIndex { .. } | Index(_) => { // The rest should only occur as mplace, we do not use Immediates for types // allowing such operations. This matches place_projection forcing an allocation. - let mplace = base.assert_mem_place(self); + let mplace = base.assert_mem_place(); self.mplace_projection(&mplace, proj_elem)?.into() } }) @@ -580,9 +610,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We rely on mutability being set correctly in that allocation to prevent writes // where none should happen. let ptr = self.global_base_pointer(Pointer::new(id, offset))?; - Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi)) + Operand::Indirect(MemPlace::from_ptr(ptr.into(), layout.align.abi)) } - ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x)?.into()), + ConstValue::Scalar(x) => Operand::Immediate(tag_scalar(x.into())?.into()), ConstValue::Slice { data, start, end } => { // We rely on mutability being set correctly in `data` to prevent writes // where none should happen. @@ -658,9 +688,9 @@ 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 tag_bits = self - .force_bits(tag_val, tag_layout.size) - .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?; + let tag_bits = tag_val + .to_bits(tag_layout.size) + .map_err(|_| err_ub!(InvalidTag(tag_val.erase_for_fmt())))?; // Cast bits from tag layout to discriminant layout. let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); let discr_bits = discr_val.assert_bits(discr_layout.size); @@ -677,7 +707,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?; + .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_for_fmt())))?; // Return the cast value, and the index. (discr_val, index.0) } @@ -691,9 +721,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end - && !self.memory.ptr_may_be_null(ptr); + && !self.memory.ptr_may_be_null(ptr.into()); if !ptr_valid { - throw_ub!(InvalidTag(tag_val.erase_tag())) + throw_ub!(InvalidTag(tag_val.erase_for_fmt())) } dataful_variant } diff --git a/compiler/rustc_mir/src/interpret/operator.rs b/compiler/rustc_mir/src/interpret/operator.rs index 3737f8781c7ae..79b493d74e19b 100644 --- a/compiler/rustc_mir/src/interpret/operator.rs +++ b/compiler/rustc_mir/src/interpret/operator.rs @@ -318,8 +318,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { right.layout.ty ); - let l = self.force_bits(left.to_scalar()?, left.layout.size)?; - let r = self.force_bits(right.to_scalar()?, right.layout.size)?; + let l = left.to_scalar()?.to_bits(left.layout.size)?; + let r = right.to_scalar()?.to_bits(right.layout.size)?; self.binary_int_op(bin_op, l, left.layout, r, right.layout) } _ if left.layout.ty.is_any_ptr() => { @@ -386,7 +386,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => { assert!(layout.ty.is_integral()); - let val = self.force_bits(val, layout.size)?; + let val = val.to_bits(layout.size)?; let (res, overflow) = match un_op { Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate Neg => { diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 42a304ce41213..b5d26306d4be3 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -3,7 +3,6 @@ //! All high-level functions to write to memory work on places as destinations. use std::convert::TryFrom; -use std::fmt::Debug; use std::hash::Hash; use rustc_ast::Mutability; @@ -15,14 +14,14 @@ use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; use super::{ - alloc_range, mir_assign_valid_types, AllocRef, AllocRefMut, ConstAlloc, ImmTy, Immediate, - InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, - PointerArithmetic, Scalar, ScalarMaybeUninit, + alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, + ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, + Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, }; -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] /// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta { +pub enum MemPlaceMeta { /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). Meta(Scalar), /// `Sized` types or unsized `extern type` @@ -35,7 +34,18 @@ pub enum MemPlaceMeta { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); +//FIXME rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); + +impl std::fmt::Debug for MemPlaceMeta { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use MemPlaceMeta::*; + match self { + Meta(s) => f.debug_tuple("Meta").field(s).finish(), + None => f.debug_tuple("None").finish(), + Poison => f.debug_tuple("Poison").finish(), + } + } +} impl MemPlaceMeta { pub fn unwrap_meta(self) -> Scalar { @@ -53,21 +63,22 @@ impl MemPlaceMeta { } } - pub fn erase_tag(self) -> MemPlaceMeta<()> { + pub fn erase_for_fmt(self) -> MemPlaceMeta + where + Tag: Provenance, + { match self { - Self::Meta(s) => MemPlaceMeta::Meta(s.erase_tag()), + Self::Meta(s) => MemPlaceMeta::Meta(s.erase_for_fmt()), Self::None => MemPlaceMeta::None, Self::Poison => MemPlaceMeta::Poison, } } } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub struct MemPlace { - /// A place may have an integral pointer for ZSTs, and since it might - /// be turned back into a reference before ever being dereferenced. - /// However, it may never be uninit. - pub ptr: Scalar, +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] +pub struct MemPlace { + /// The pointer can be a pure integer, with the `None` tag. + pub ptr: Pointer>, pub align: Align, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types @@ -76,10 +87,21 @@ pub struct MemPlace { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MemPlace, 56); +//FIXME rustc_data_structures::static_assert_size!(MemPlace, 56); + +impl std::fmt::Debug for MemPlace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let MemPlace { ptr, align, meta } = self; + f.debug_struct("MemPlace") + .field("ptr", ptr) + .field("align", align) + .field("meta", meta) + .finish() + } +} -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] -pub enum Place { +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] +pub enum Place { /// A place referring to a value allocated in the `Memory` system. Ptr(MemPlace), @@ -89,16 +111,35 @@ pub enum Place { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Place, 64); +//FIXME rustc_data_structures::static_assert_size!(Place, 64); + +impl std::fmt::Debug for Place { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Place::*; + match self { + Ptr(p) => f.debug_tuple("Ptr").field(p).finish(), + Local { frame, local } => { + f.debug_struct("Local").field("frame", frame).field("local", local).finish() + } + } + } +} -#[derive(Copy, Clone, Debug)] -pub struct PlaceTy<'tcx, Tag = ()> { +#[derive(Copy, Clone)] +pub struct PlaceTy<'tcx, Tag = AllocId> { place: Place, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80); +//FIXME rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80); + +impl<'tcx, Tag: Provenance> std::fmt::Debug for PlaceTy<'tcx, Tag> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let PlaceTy { place, layout } = self; + f.debug_struct("PlaceTy").field("place", place).field("layout", layout).finish() + } +} impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { type Target = Place; @@ -109,14 +150,21 @@ impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { } /// A MemPlace with its layout. Constructing it is only possible in this module. -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub struct MPlaceTy<'tcx, Tag = ()> { +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct MPlaceTy<'tcx, Tag = AllocId> { mplace: MemPlace, pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72); +//FIXME rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72); + +impl<'tcx, Tag: Provenance> std::fmt::Debug for MPlaceTy<'tcx, Tag> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let MPlaceTy { mplace, layout } = self; + f.debug_struct("MPlaceTy").field("mplace", mplace).field("layout", layout).finish() + } +} impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> { type Target = MemPlace; @@ -134,34 +182,32 @@ impl<'tcx, Tag> From> for PlaceTy<'tcx, Tag> { } impl MemPlace { - /// Replace ptr tag, maintain vtable tag (if any) - #[inline] - pub fn replace_tag(self, new_tag: Tag) -> Self { - MemPlace { ptr: self.ptr.erase_tag().with_tag(new_tag), align: self.align, meta: self.meta } - } - #[inline] - pub fn erase_tag(self) -> MemPlace { - MemPlace { ptr: self.ptr.erase_tag(), align: self.align, meta: self.meta.erase_tag() } + pub fn erase_for_fmt(self) -> MemPlace + where + Tag: Provenance, + { + MemPlace { + ptr: self.ptr.map_erase_for_fmt(), + align: self.align, + meta: self.meta.erase_for_fmt(), + } } #[inline(always)] - fn from_scalar_ptr(ptr: Scalar, align: Align) -> Self { + pub fn from_ptr(ptr: Pointer>, align: Align) -> Self { MemPlace { ptr, align, meta: MemPlaceMeta::None } } - #[inline(always)] - pub fn from_ptr(ptr: Pointer, align: Align) -> Self { - Self::from_scalar_ptr(ptr.into(), align) - } - /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. /// This is the inverse of `ref_to_mplace`. #[inline(always)] - pub fn to_ref(self) -> Immediate { + pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate { match self.meta { - MemPlaceMeta::None => Immediate::Scalar(self.ptr.into()), - MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()), + MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)), + MemPlaceMeta::Meta(meta) => { + Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into()) + } MemPlaceMeta::Poison => bug!( "MPlaceTy::dangling may never be used to produce a \ place that will have the address of its pointee taken" @@ -177,7 +223,7 @@ impl MemPlace { cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MemPlace { - ptr: self.ptr.ptr_offset(offset, cx)?, + ptr: self.ptr.offset(offset, cx)?, align: self.align.restrict_for_offset(offset), meta, }) @@ -187,19 +233,13 @@ impl MemPlace { impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { /// Produces a MemPlace that works for ZST but nothing else #[inline] - pub fn dangling(layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { + pub fn dangling(layout: TyAndLayout<'tcx>) -> Self { let align = layout.align.abi; - let ptr = Scalar::from_machine_usize(align.bytes(), cx); + let ptr = Pointer::new(None, Size::from_bytes(align.bytes())); // no provenance, absolute address // `Poison` this to make sure that the pointer value `ptr` is never observable by the program. MPlaceTy { mplace: MemPlace { ptr, align, meta: MemPlaceMeta::Poison }, layout } } - /// Replace ptr tag, maintain vtable tag (if any) - #[inline] - pub fn replace_tag(&self, new_tag: Tag) -> Self { - MPlaceTy { mplace: self.mplace.replace_tag(new_tag), layout: self.layout } - } - #[inline] pub fn offset( &self, @@ -212,12 +252,15 @@ impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { } #[inline] - fn from_aligned_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> Self { + fn from_aligned_ptr(ptr: Pointer>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } } #[inline] - pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { + pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> + where + Tag: Provenance, + { if self.layout.is_unsized() { // We need to consult `meta` metadata match self.layout.ty.kind() { @@ -244,19 +287,14 @@ impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { } // These are defined here because they produce a place. -impl<'tcx, Tag: Debug + Copy> OpTy<'tcx, Tag> { +impl<'tcx, Tag: Copy> OpTy<'tcx, Tag> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn try_as_mplace( - &self, - cx: &impl HasDataLayout, - ) -> Result, ImmTy<'tcx, Tag>> { + pub fn try_as_mplace(&self) -> Result, ImmTy<'tcx, Tag>> { match **self { Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }), - Operand::Immediate(_) if self.layout.is_zst() => { - Ok(MPlaceTy::dangling(self.layout, cx)) - } + Operand::Immediate(_) if self.layout.is_zst() => Ok(MPlaceTy::dangling(self.layout)), Operand::Immediate(imm) => Err(ImmTy::from_immediate(imm, self.layout)), } } @@ -264,12 +302,15 @@ impl<'tcx, Tag: Debug + Copy> OpTy<'tcx, Tag> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn assert_mem_place(&self, cx: &impl HasDataLayout) -> MPlaceTy<'tcx, Tag> { - self.try_as_mplace(cx).unwrap() + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> + where + Tag: Provenance, + { + self.try_as_mplace().unwrap() } } -impl Place { +impl Place { #[inline] pub fn assert_mem_place(self) -> MemPlace { match self { @@ -279,7 +320,7 @@ impl Place { } } -impl<'tcx, Tag: Debug> PlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { #[inline] pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout } @@ -290,7 +331,7 @@ impl<'tcx, Tag: Debug> PlaceTy<'tcx, Tag> { impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> where // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 - Tag: Debug + Copy + Eq + Hash + 'static, + Tag: Provenance + Eq + Hash + 'static, M: Machine<'mir, 'tcx, PointerTag = Tag>, { /// Take a value, which represents a (thin or wide) reference, and make it a place. @@ -307,14 +348,12 @@ where val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type").ty; let layout = self.layout_of(pointee_type)?; let (ptr, meta) = match **val { - Immediate::Scalar(ptr) => (ptr.check_init()?, MemPlaceMeta::None), - Immediate::ScalarPair(ptr, meta) => { - (ptr.check_init()?, MemPlaceMeta::Meta(meta.check_init()?)) - } + Immediate::Scalar(ptr) => (ptr, MemPlaceMeta::None), + Immediate::ScalarPair(ptr, meta) => (ptr, MemPlaceMeta::Meta(meta.check_init()?)), }; let mplace = MemPlace { - ptr, + ptr: self.scalar_to_ptr(ptr.check_init()?), // We could use the run-time alignment here. For now, we do not, because // the point of tracking the alignment here is to make sure that the *static* // alignment information emitted with the loads is correct. The run-time @@ -333,8 +372,9 @@ where ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); - let place = self.ref_to_mplace(&val)?; - self.mplace_access_checked(place, None) + let mplace = self.ref_to_mplace(&val)?; + self.check_mplace_access(mplace)?; + Ok(mplace) } #[inline] @@ -359,38 +399,20 @@ where self.memory.get_mut(place.ptr, size, place.align) } - /// Return the "access-checked" version of this `MPlace`, where for non-ZST - /// this is definitely a `Pointer`. - /// - /// `force_align` must only be used when correct alignment does not matter, - /// like in Stacked Borrows. - pub fn mplace_access_checked( - &self, - mut place: MPlaceTy<'tcx, M::PointerTag>, - force_align: Option, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + /// Check if this mplace is dereferencable and sufficiently aligned. + pub fn check_mplace_access(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { let (size, align) = self - .size_and_align_of_mplace(&place)? - .unwrap_or((place.layout.size, place.layout.align.abi)); - assert!(place.mplace.align <= align, "dynamic alignment less strict than static one?"); - let align = force_align.unwrap_or(align); - // Record new (stricter, unless forced) alignment requirement in place. - place.mplace.align = align; - // When dereferencing a pointer, it must be non-null, aligned, and live. - if let Some(ptr) = self.memory.check_ptr_access(place.ptr, size, align)? { - place.mplace.ptr = ptr.into(); - } - Ok(place) - } - - /// Force `place.ptr` to a `Pointer`. - /// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot. - pub(super) fn force_mplace_ptr( - &self, - mut place: MPlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - place.mplace.ptr = self.force_ptr(place.mplace.ptr)?.into(); - Ok(place) + .size_and_align_of_mplace(&mplace)? + .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); + assert!(mplace.mplace.align <= align, "dynamic alignment less strict than static one?"); + let align = M::enforce_alignment(&self.memory.extra).then_some(align); + self.memory.check_ptr_access_align( + mplace.ptr, + size, + align.unwrap_or(Align::ONE), + CheckInAllocMsg::MemoryAccessTest, // FIXME sth more specific? + )?; + Ok(()) } /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is @@ -558,10 +580,7 @@ where let layout = self.layout_of(self.tcx.types.usize)?; let n = self.access_local(self.frame(), local, Some(layout))?; let n = self.read_scalar(&n)?; - let n = u64::try_from( - self.force_bits(n.check_init()?, self.tcx.data_layout.pointer_size)?, - ) - .unwrap(); + let n = n.to_machine_usize(self)?; self.mplace_index(base, n)? } @@ -1020,7 +1039,7 @@ where kind: MemoryKind, ) -> InterpResult<'static, MPlaceTy<'tcx, M::PointerTag>> { let ptr = self.memory.allocate(layout.size, layout.align.abi, kind)?; - Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) + Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) } /// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation. @@ -1125,7 +1144,7 @@ where let _ = self.tcx.global_alloc(raw.alloc_id); let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?; let layout = self.layout_of(raw.ty)?; - Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) + Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout)) } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. @@ -1134,7 +1153,7 @@ where &self, mplace: &MPlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { - let vtable = 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 layout = self.layout_of(ty)?; diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 129dd8f8e0125..2fbcfcfbe9087 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -240,7 +240,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // of the first element. let elem_size = first.layout.size; let first_ptr = first.ptr; - let rest_ptr = first_ptr.ptr_offset(elem_size, self)?; + let rest_ptr = first_ptr.offset(elem_size, self)?; self.memory.copy_repeatedly( first_ptr, first.align, @@ -264,11 +264,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { AddressOf(_, place) | Ref(_, _, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; - if place.layout.size.bytes() > 0 { - // definitely not a ZST - assert!(place.ptr.is_ptr(), "non-ZST places should be normalized to `Pointer`"); - } - self.write_immediate(place.to_ref(), &dest)?; + self.write_immediate(place.to_ref(self), &dest)?; } NullaryOp(mir::NullOp::Box, _) => { diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index aea9933b337c9..f369480d959fe 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -12,8 +12,8 @@ use rustc_target::abi::{self, LayoutOf as _}; use rustc_target::spec::abi::Abi; use super::{ - FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, StackPopCleanup, - StackPopUnwind, + FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Scalar, + StackPopCleanup, StackPopUnwind, }; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -72,8 +72,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (fn_val, abi, caller_can_unwind) = match *func.layout.ty.kind() { ty::FnPtr(sig) => { let caller_abi = sig.abi(); - let fn_ptr = self.read_scalar(&func)?.check_init()?; - let fn_val = self.memory.get_fn(fn_ptr)?; + let fn_ptr = self.read_pointer(&func)?; + let fn_val = self.memory.get_fn(fn_ptr.into())?; ( fn_val, caller_abi, @@ -454,11 +454,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } None => { // Unsized self. - args[0].assert_mem_place(self) + args[0].assert_mem_place() } }; // Find and consult vtable - let vtable = receiver_place.vtable(); + let vtable = self.scalar_to_ptr(receiver_place.vtable()); let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?; // `*mut receiver_place.layout.ty` is almost the layout that we @@ -468,8 +468,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { 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(receiver_place.ptr.into(), this_receiver_ptr)); + args[0] = OpTy::from(ImmTy::from_immediate( + Scalar::from_maybe_pointer(receiver_place.ptr, self).into(), + this_receiver_ptr, + )); trace!("Patched self operand to {:#?}", args[0]); // recurse with concrete function self.eval_fn_call(fn_val, caller_abi, &args, ret, unwind) @@ -499,12 +501,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; let arg = ImmTy::from_immediate( - place.to_ref(), + place.to_ref(self), self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, ); let ty = self.tcx.mk_unit(); // return type is () - let dest = MPlaceTy::dangling(self.layout_of(ty)?, self); + let dest = MPlaceTy::dangling(self.layout_of(ty)?); self.eval_fn_call( FnVal::Instance(instance), diff --git a/compiler/rustc_mir/src/interpret/traits.rs b/compiler/rustc_mir/src/interpret/traits.rs index 5332e615bc8ea..7a93fcee78e33 100644 --- a/compiler/rustc_mir/src/interpret/traits.rs +++ b/compiler/rustc_mir/src/interpret/traits.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar}; +use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic}; use rustc_middle::ty::{ self, Ty, COMMON_VTABLE_ENTRIES, COMMON_VTABLE_ENTRIES_ALIGN, COMMON_VTABLE_ENTRIES_DROPINPLACE, COMMON_VTABLE_ENTRIES_SIZE, @@ -42,23 +42,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// corresponds to the first method declared in the trait of the provided vtable. pub fn get_vtable_slot( &self, - vtable: Scalar, + vtable: Pointer>, idx: u64, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr_size = self.pointer_size(); - let vtable_slot = vtable.ptr_offset(ptr_size * idx, self)?; + let vtable_slot = vtable.offset(ptr_size * idx, self)?; let vtable_slot = self .memory .get(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)? .expect("cannot be a ZST"); - let fn_ptr = vtable_slot.read_ptr_sized(Size::ZERO)?.check_init()?; + let fn_ptr = self.scalar_to_ptr(vtable_slot.read_ptr_sized(Size::ZERO)?.check_init()?); self.memory.get_fn(fn_ptr) } /// Returns the drop fn instance as well as the actual dynamic type. pub fn read_drop_type_from_vtable( &self, - vtable: Scalar, + vtable: Pointer>, ) -> 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. @@ -77,7 +77,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .check_init()?; // We *need* an instance here, no other kind of function value, to be able // to determine the type. - let drop_instance = self.memory.get_fn(drop_fn)?.as_instance()?; + let drop_instance = self.memory.get_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); @@ -93,7 +93,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn read_size_and_align_from_vtable( &self, - vtable: Scalar, + vtable: Pointer>, ) -> InterpResult<'tcx, (Size, Align)> { let pointer_size = self.pointer_size(); // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here), @@ -109,11 +109,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = vtable .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap())? .check_init()?; - let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap(); + let size = size.to_machine_usize(self)?; let align = vtable .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap())? .check_init()?; - let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap(); + let align = align.to_machine_usize(self)?; let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?; if size >= self.tcx.data_layout.obj_size_bound() { diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 112cba3efda84..703c748024824 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -21,7 +21,7 @@ use std::hash::Hash; use super::{ alloc_range, CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, - MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor, + MemPlaceMeta, OpTy, ScalarMaybeUninit, ValueVisitor, }; macro_rules! throw_validation_failure { @@ -324,7 +324,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { ty::Dynamic(..) => { - let vtable = 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. try_validation!( self.ecx.memory.check_ptr_access_align( @@ -448,17 +448,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' if let Some(ref mut ref_tracking) = self.ref_tracking { // Proceed recursively even for ZST, no reason to skip them! // `!` is a ZST and we want to validate it. - // Normalize before handing `place` to tracking because that will - // check for duplicates. - let place = if size.bytes() > 0 { - self.ecx.force_mplace_ptr(place).expect("we already bounds-checked") - } else { - place - }; // Skip validation entirely for some external statics - if let Scalar::Ptr(ptr) = place.ptr { + if let Ok((alloc_id, _offset, _ptr)) = self.ecx.memory.ptr_try_get_alloc(place.ptr) { // not a ZST - let alloc_kind = self.ecx.tcx.get_global_alloc(ptr.alloc_id); + let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id); if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); @@ -601,7 +594,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // message below. let value = value.to_scalar_or_uninit(); let _fn = try_validation!( - value.check_init().and_then(|ptr| self.ecx.memory.get_fn(ptr)), + value.check_init().and_then(|ptr| self.ecx.memory.get_fn(self.ecx.scalar_to_ptr(ptr))), self.path, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) | @@ -668,7 +661,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Err(ptr) => { if lo == 1 && hi == max_hi { // Only null is the niche. So make sure the ptr is NOT null. - if self.ecx.memory.ptr_may_be_null(ptr) { + if self.ecx.memory.ptr_may_be_null(ptr.into()) { throw_validation_failure!(self.path, { "a potentially null pointer" } expected { @@ -832,7 +825,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ) -> InterpResult<'tcx> { match op.layout.ty.kind() { ty::Str => { - let mplace = op.assert_mem_place(self.ecx); // strings are never immediate + let mplace = op.assert_mem_place(); // strings are never immediate let len = mplace.len(self.ecx)?; try_validation!( self.ecx.memory.read_bytes(mplace.ptr, Size::from_bytes(len)), @@ -853,7 +846,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Optimized handling for arrays of integer/float type. // Arrays cannot be immediate, slices are never immediate. - let mplace = op.assert_mem_place(self.ecx); + let mplace = op.assert_mem_place(); // This is the length of the array/slice. let len = mplace.len(self.ecx)?; // This is the element type size. @@ -940,9 +933,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Construct a visitor let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self }; - // Try to cast to ptr *once* instead of all the time. - let op = self.force_op_ptr(&op).unwrap_or(*op); - // Run it. match visitor.visit_value(&op) { Ok(()) => Ok(()), diff --git a/compiler/rustc_mir/src/interpret/visitor.rs b/compiler/rustc_mir/src/interpret/visitor.rs index 32edca6f3dff9..679d30227f136 100644 --- a/compiler/rustc_mir/src/interpret/visitor.rs +++ b/compiler/rustc_mir/src/interpret/visitor.rs @@ -211,7 +211,8 @@ macro_rules! make_value_visitor { // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { // immediate trait objects are not a thing - let dest = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); + let op = v.to_op(self.ecx())?; + let dest = op.assert_mem_place(); let inner = self.ecx().unpack_dyn_trait(&dest)?.1; trace!("walk_value: dyn object layout: {:#?}", inner.layout); // recurse with the inner type @@ -241,7 +242,8 @@ macro_rules! make_value_visitor { }, FieldsShape::Array { .. } => { // Let's get an mplace first. - let mplace = v.to_op(self.ecx())?.assert_mem_place(self.ecx()); + let op = v.to_op(self.ecx())?; + let mplace = op.assert_mem_place(); // Now we can go over all the fields. // This uses the *run-time length*, i.e., if we are a slice, // the dynamic info from the metadata is used. diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index ced35d47b1102..548518a85e6fe 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -403,7 +403,7 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = None; if let Ok(alloc) = tcx.eval_static_initializer(def_id) { - for &((), id) in alloc.relocations().values() { + for &id in alloc.relocations().values() { collect_miri(tcx, id, &mut neighbors); } } @@ -1369,7 +1369,7 @@ fn collect_miri<'tcx>( } GlobalAlloc::Memory(alloc) => { trace!("collecting {:?} with {:#?}", alloc_id, alloc); - for &((), inner) in alloc.relocations().values() { + for &inner in alloc.relocations().values() { rustc_data_structures::stack::ensure_sufficient_stack(|| { collect_miri(tcx, inner, output); }); @@ -1402,9 +1402,9 @@ fn collect_const_value<'tcx>( output: &mut Vec>>, ) { match value { - ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output), + ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.provenance, output), ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => { - for &((), id) in alloc.relocations().values() { + for &id in alloc.relocations().values() { collect_miri(tcx, id, output); } } diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index e9b68754bdf80..bbf3a1c669f3a 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -31,9 +31,8 @@ use rustc_trait_selection::traits; use crate::const_eval::ConstEvalErr; use crate::interpret::{ self, compile_time_machine, AllocId, Allocation, ConstValue, CtfeValidationMode, Frame, ImmTy, - Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, - Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, - StackPopUnwind, + Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy, + Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, }; use crate::transform::MirPass; @@ -157,7 +156,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { struct ConstPropMachine<'mir, 'tcx> { /// The virtual call stack. - stack: Vec>, + stack: Vec>, /// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end. written_only_inside_own_block_locals: FxHashSet, /// Locals that need to be cleared after every block terminates. @@ -223,10 +222,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> bug!("panics terminators are not evaluated in ConstProp") } - fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { - throw_unsup!(ReadPointerAsBytes) - } - fn binary_ptr_op( _ecx: &InterpCx<'mir, 'tcx, Self>, _bin_op: BinOp, @@ -759,8 +754,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } }; - let arg_value = - this.ecx.force_bits(const_arg.to_scalar()?, const_arg.layout.size)?; + let arg_value = const_arg.to_scalar()?.to_bits(const_arg.layout.size)?; let dest = this.ecx.eval_place(place)?; match op { diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs index 9f473f3bae534..0380218ec57f1 100644 --- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -211,7 +211,7 @@ fn find_branch_value_info<'tcx>( return None; }; let branch_value_scalar = branch_value.literal.try_to_scalar()?; - Some((branch_value_scalar, branch_value_ty, *to_switch_on)) + Some((branch_value_scalar.into(), branch_value_ty, *to_switch_on)) } _ => None, } diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index 17401c24dddfb..f31e2feac3118 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; +use std::fmt::Display; use std::fmt::Write as _; -use std::fmt::{Debug, Display}; use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{ - read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, + read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, Provenance, }; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -665,12 +665,12 @@ pub fn write_allocations<'tcx>( w: &mut dyn Write, ) -> io::Result<()> { fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator + '_ { - alloc.relocations().values().map(|(_, id)| *id) + alloc.relocations().values().map(|id| *id) } fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { match val { ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { - Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) + Either::Left(Either::Left(std::iter::once(ptr.provenance))) } ConstValue::Scalar(interpret::Scalar::Int { .. }) => { Either::Left(Either::Right(std::iter::empty())) @@ -755,7 +755,7 @@ pub fn write_allocations<'tcx>( /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control /// characters or characters whose value is larger than 127) with a `.` /// This also prints relocations adequately. -pub fn display_allocation( +pub fn display_allocation( tcx: TyCtxt<'tcx>, alloc: &'a Allocation, ) -> RenderAllocation<'a, 'tcx, Tag, Extra> { @@ -768,7 +768,7 @@ pub struct RenderAllocation<'a, 'tcx, Tag, Extra> { alloc: &'a Allocation, } -impl std::fmt::Display for RenderAllocation<'a, 'tcx, Tag, Extra> { +impl std::fmt::Display for RenderAllocation<'a, 'tcx, Tag, Extra> { fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let RenderAllocation { tcx, alloc } = *self; write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?; @@ -811,7 +811,7 @@ fn write_allocation_newline( /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there /// is only one line). Note that your prefix should contain a trailing space as the lines are /// printed directly after it. -fn write_allocation_bytes( +fn write_allocation_bytes( tcx: TyCtxt<'tcx>, alloc: &Allocation, w: &mut dyn std::fmt::Write, @@ -847,7 +847,7 @@ fn write_allocation_bytes( if i != line_start { write!(w, " ")?; } - if let Some(&(tag, target_id)) = alloc.relocations().get(&i) { + if let Some(&tag) = alloc.relocations().get(&i) { // Memory with a relocation must be defined let j = i.bytes_usize(); let offset = alloc @@ -855,7 +855,7 @@ fn write_allocation_bytes( let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap(); let offset = Size::from_bytes(offset); let relocation_width = |bytes| bytes * 3; - let ptr = Pointer::new_with_tag(target_id, offset, tag); + let ptr = Pointer::new(tag, offset); let mut target = format!("{:?}", ptr); if target.len() > relocation_width(ptr_size.bytes_usize() - 1) { // This is too long, try to save some space. From 626605cea02ee5f512f5efae0cd188ae1a7007c7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 12 Jul 2021 20:29:05 +0200 Subject: [PATCH 02/16] consistently treat None-tagged pointers as ints; get rid of some deprecated Scalar methods --- compiler/rustc_codegen_llvm/src/common.rs | 2 +- compiler/rustc_codegen_llvm/src/consts.rs | 7 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 8 +- .../src/mir/interpret/allocation.rs | 4 +- .../rustc_middle/src/mir/interpret/pointer.rs | 2 +- .../rustc_middle/src/mir/interpret/value.rs | 145 ++++++++---------- compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/relate.rs | 5 +- compiler/rustc_middle/src/ty/vtable.rs | 6 +- .../rustc_mir/src/const_eval/eval_queries.rs | 2 + compiler/rustc_mir/src/const_eval/machine.rs | 10 +- compiler/rustc_mir/src/const_eval/mod.rs | 2 +- compiler/rustc_mir/src/interpret/cast.rs | 6 +- .../rustc_mir/src/interpret/eval_context.rs | 6 +- .../rustc_mir/src/interpret/intrinsics.rs | 6 +- compiler/rustc_mir/src/interpret/machine.rs | 3 + compiler/rustc_mir/src/interpret/memory.rs | 5 +- compiler/rustc_mir/src/interpret/operand.rs | 28 ++-- compiler/rustc_mir/src/interpret/place.rs | 2 +- compiler/rustc_mir/src/interpret/step.rs | 2 +- compiler/rustc_mir/src/interpret/validity.rs | 4 +- .../rustc_mir/src/monomorphize/collector.rs | 2 +- .../rustc_mir/src/transform/const_prop.rs | 7 +- .../transform/simplify_comparison_integral.rs | 2 +- compiler/rustc_mir/src/util/pretty.rs | 2 +- compiler/rustc_mir_build/src/thir/constant.rs | 2 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 6 +- .../src/thir/pattern/deconstruct_pat.rs | 2 +- 29 files changed, 144 insertions(+), 138 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index a73932ee1b57f..35e72621c565d 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -243,7 +243,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) } } - Scalar::Ptr(ptr) => { + Scalar::Ptr(ptr, _size) => { let (alloc_id, offset) = ptr.into_parts(); let (base_addr, base_addr_space) = match self.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 71a387bfcacd9..3ca295f4a7e89 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -11,7 +11,7 @@ use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer, + read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer, Scalar as InterpScalar, }; use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; @@ -55,7 +55,10 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll }; llvals.push(cx.scalar_to_backend( - Pointer::new(alloc_id, Size::from_bytes(ptr_offset)).into(), + InterpScalar::from_pointer( + Pointer::new(alloc_id, Size::from_bytes(ptr_offset)), + &cx.tcx, + ), &Scalar { value: Primitive::Pointer, valid_range: 0..=!0 }, cx.type_i8p_ext(address_space), )); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 3c42b2cc2ea7a..3e8386bc88fed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -90,10 +90,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { Abi::ScalarPair(ref a, _) => a, _ => bug!("from_const: invalid ScalarPair layout: {:#?}", layout), }; - let a = Scalar::from(Pointer::new( - bx.tcx().create_memory_alloc(data), - Size::from_bytes(start), - )); + let a = Scalar::from_pointer( + Pointer::new(bx.tcx().create_memory_alloc(data), Size::from_bytes(start)), + &bx.tcx(), + ); let a_llval = bx.scalar_to_backend( a, a_scalar, diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index f9840e35bc60d..1f2227f6ea02c 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -333,7 +333,7 @@ impl Allocation { // Maybe a pointer. if let Some(&prov) = self.relocations.get(&range.start) { let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(ScalarMaybeUninit::Scalar(ptr.into())); + return Ok(ScalarMaybeUninit::from_pointer(ptr, cx)); } } // We don't. Just return the bits. @@ -363,7 +363,7 @@ impl Allocation { } }; - let (bytes, provenance) = match val.to_bits_or_ptr(range.size, cx) { + let (bytes, provenance) = match val.to_bits_or_ptr(range.size) { Err(val) => { let (provenance, offset) = val.into_parts(); (u128::from(offset.bytes()), Some(provenance)) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c9dc96fc88e02..3d6ee49a19e0f 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -87,7 +87,7 @@ impl PointerArithmetic for T {} /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. pub trait Provenance: Copy { - /// Says whether the `offset` field of `Pointer` is the actual physical address. + /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// If `true, ptr-to-int casts work by simply discarding the provenance. /// If `false`, ptr-to-int casts are not supported. const OFFSET_IS_ADDR: bool; diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 29692c07f031c..831a4d33ca20b 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -128,7 +128,11 @@ pub enum Scalar { /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the /// relocation and its associated offset together as a `Pointer` here. - Ptr(Pointer), + /// + /// We also store the size of the pointer, such that a `Scalar` always knows how big it is. + /// The size is always the pointer size of the current target, but this is not information + /// that we always have readily available. + Ptr(Pointer, u8), } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -139,7 +143,7 @@ pub enum Scalar { impl fmt::Debug for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr) => write!(f, "{:?}", ptr), + Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr), Scalar::Int(int) => write!(f, "{:?}", int), } } @@ -148,7 +152,7 @@ impl fmt::Debug for Scalar { impl fmt::Display for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr) => write!(f, "pointer to {:?}", ptr), + Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), Scalar::Int(int) => write!(f, "{:?}", int), } } @@ -168,13 +172,6 @@ impl From for Scalar { } } -impl From> for Scalar { - #[inline(always)] - fn from(ptr: Pointer) -> Self { - Scalar::Ptr(ptr) - } -} - impl From for Scalar { #[inline(always)] fn from(ptr: ScalarInt) -> Self { @@ -185,21 +182,26 @@ impl From for Scalar { impl<'tcx, Tag> Scalar { pub const ZST: Self = Scalar::Int(ScalarInt::ZST); - #[inline] - pub fn null_ptr(cx: &impl HasDataLayout) -> Self { - Scalar::Int(ScalarInt::null(cx.data_layout().pointer_size)) + #[inline(always)] + pub fn from_pointer(ptr: Pointer, cx: &impl HasDataLayout) -> Self { + Scalar::Ptr(ptr, u8::try_from(cx.pointer_size().bytes()).unwrap()) } /// Create a Scalar from a pointer with an `Option<_>` tag (where `None` represents a plain integer). pub fn from_maybe_pointer(ptr: Pointer>, cx: &impl HasDataLayout) -> Self { match ptr.into_parts() { - (Some(tag), offset) => Scalar::Ptr(Pointer::new(tag, offset)), + (Some(tag), offset) => Scalar::from_pointer(Pointer::new(tag, offset), cx), (None, offset) => { Scalar::Int(ScalarInt::try_from_uint(offset.bytes(), cx.pointer_size()).unwrap()) } } } + #[inline] + pub fn null_ptr(cx: &impl HasDataLayout) -> Self { + Scalar::Int(ScalarInt::null(cx.pointer_size())) + } + #[inline(always)] fn ptr_op( self, @@ -209,7 +211,10 @@ impl<'tcx, Tag> Scalar { ) -> InterpResult<'tcx, Self> { match self { Scalar::Int(int) => Ok(Scalar::Int(int.ptr_sized_op(dl, f_int)?)), - Scalar::Ptr(ptr) => Ok(Scalar::Ptr(f_ptr(ptr)?)), + Scalar::Ptr(ptr, sz) => { + debug_assert_eq!(u64::from(sz), dl.pointer_size().bytes()); + Ok(Scalar::Ptr(f_ptr(ptr)?, sz)) + } } } @@ -334,59 +339,18 @@ impl<'tcx, Tag> Scalar { /// This is almost certainly not the method you want! You should dispatch on the type /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed. /// - /// This method only exists for the benefit of low-level memory operations - /// as well as the implementation of the above methods. + /// This method only exists for the benefit of low-level memory operations. #[inline] - pub fn to_bits_or_ptr( - self, - target_size: Size, - cx: &impl HasDataLayout, - ) -> Result> { + pub fn to_bits_or_ptr(self, target_size: Size) -> Result> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { Scalar::Int(int) => Ok(int.assert_bits(target_size)), - Scalar::Ptr(ptr) => { - assert_eq!(target_size, cx.data_layout().pointer_size); + Scalar::Ptr(ptr, sz) => { + assert_eq!(target_size.bytes(), u64::from(sz)); Err(ptr) } } } - - /// Do not call this method! It does not do ptr-to-int casts when needed. - #[inline(always)] - pub fn assert_bits(self, target_size: Size) -> u128 { - self.assert_int().assert_bits(target_size) - } - - /// Do not call this method! It does not do ptr-to-int casts when needed. - #[inline] - pub fn assert_int(self) -> ScalarInt { - match self { - Scalar::Ptr(_) => bug!("expected an int but got an abstract pointer"), - Scalar::Int(int) => int, - } - } - - /// Do not call this method! It does not do int-to-ptr casts when needed. - #[inline] - pub fn assert_ptr(self) -> Pointer { - match self { - Scalar::Ptr(p) => p, - Scalar::Int { .. } => bug!("expected a Pointer but got Raw bits"), - } - } - - /// Do not call this method! Dispatch based on the type instead. - #[inline] - pub fn is_bits(self) -> bool { - matches!(self, Scalar::Int { .. }) - } - - /// Do not call this method! Dispatch based on the type instead. - #[inline] - pub fn is_ptr(self) -> bool { - matches!(self, Scalar::Ptr(_)) - } } impl<'tcx, Tag: Provenance> Scalar { @@ -396,7 +360,7 @@ impl<'tcx, Tag: Provenance> Scalar { #[inline] pub fn erase_for_fmt(self) -> Scalar { match self { - Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_for_fmt()), + Scalar::Ptr(ptr, sz) => Scalar::Ptr(ptr.erase_for_fmt(), sz), Scalar::Int(int) => Scalar::Int(int), } } @@ -405,25 +369,45 @@ impl<'tcx, Tag: Provenance> Scalar { /// likely want to use instead. /// /// Will perform ptr-to-int casts if needed and possible. + #[inline] + pub fn try_to_int(self) -> Option { + match self { + Scalar::Int(int) => Some(int), + Scalar::Ptr(ptr, sz) => { + if Tag::OFFSET_IS_ADDR { + Some( + ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap(), + ) + } else { + None + } + } + } + } + + #[inline(always)] + pub fn assert_int(self) -> ScalarInt { + self.try_to_int().unwrap() + } + #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - match self { - Scalar::Int(int) => int.to_bits(target_size).map_err(|size| { + self.try_to_int() + .ok_or_else(|| err_unsup!(ReadPointerAsBytes))? + .to_bits(target_size) + .map_err(|size| { err_ub!(ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes(), }) .into() - }), - Scalar::Ptr(ptr) => { - if Tag::OFFSET_IS_ADDR { - Ok(ptr.offset.bytes().into()) - } else { - throw_unsup!(ReadPointerAsBytes) - } - } - } + }) + } + + #[inline(always)] + pub fn assert_bits(self, target_size: Size) -> u128 { + self.to_bits(target_size).unwrap() } pub fn to_bool(self) -> InterpResult<'tcx, bool> { @@ -547,13 +531,6 @@ impl From> for ScalarMaybeUninit { } } -impl From> for ScalarMaybeUninit { - #[inline(always)] - fn from(s: Pointer) -> Self { - ScalarMaybeUninit::Scalar(s.into()) - } -} - // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. impl fmt::Debug for ScalarMaybeUninit { @@ -575,6 +552,16 @@ impl fmt::Display for ScalarMaybeUninit { } impl ScalarMaybeUninit { + #[inline] + pub fn from_pointer(ptr: Pointer, cx: &impl HasDataLayout) -> Self { + ScalarMaybeUninit::Scalar(Scalar::from_pointer(ptr, cx)) + } + + #[inline] + pub fn from_maybe_pointer(ptr: Pointer>, cx: &impl HasDataLayout) -> Self { + ScalarMaybeUninit::Scalar(Scalar::from_maybe_pointer(ptr, cx)) + } + #[inline] pub fn check_init(self) -> InterpResult<'static, Scalar> { match self { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 9fc02a590c35c..da0d2575dcbe3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2458,7 +2458,7 @@ pub enum ConstantKind<'tcx> { impl Constant<'tcx> { pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { match self.literal.const_for_ty()?.val.try_to_scalar() { - Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.provenance) { + Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) { GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); Some(def_id) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 25058d2cef715..dbdca44952f45 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -974,7 +974,7 @@ pub trait PrettyPrinter<'tcx>: print_ty: bool, ) -> Result { match scalar { - Scalar::Ptr(ptr) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty), + Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty), Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty), } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 5d1cc1242760c..a4c36be21992b 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -595,7 +595,10 @@ fn check_const_value_eq>( (ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => { a_val == b_val } - (ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => { + ( + ConstValue::Scalar(Scalar::Ptr(a_val, _a_size)), + ConstValue::Scalar(Scalar::Ptr(b_val, _b_size)), + ) => { a_val == b_val || match (tcx.global_alloc(a_val.provenance), tcx.global_alloc(b_val.provenance)) { (GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => { diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 0940137a7c33f..78109fc7b5713 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar}; +use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar, ScalarMaybeUninit}; use crate::ty::fold::TypeFoldable; use crate::ty::{self, DefId, SubstsRef, Ty, TyCtxt}; use rustc_ast::Mutability; @@ -74,7 +74,7 @@ impl<'tcx> TyCtxt<'tcx> { let instance = ty::Instance::resolve_drop_in_place(tcx, ty); let fn_alloc_id = tcx.create_fn_alloc(instance); let fn_ptr = Pointer::from(fn_alloc_id); - fn_ptr.into() + ScalarMaybeUninit::from_pointer(fn_ptr, &tcx) } VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size).into(), VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size).into(), @@ -90,7 +90,7 @@ impl<'tcx> TyCtxt<'tcx> { .polymorphize(tcx); let fn_alloc_id = tcx.create_fn_alloc(instance); let fn_ptr = Pointer::from(fn_alloc_id); - fn_ptr.into() + ScalarMaybeUninit::from_pointer(fn_ptr, &tcx) } }; vtable diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index 00d73d42cfcd8..dc6a6b0b9f3d8 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -139,6 +139,7 @@ pub(super) fn op_to_const<'tcx>( op.try_as_mplace() }; + // We know `offset` is relative to the allocation, so we can use `into_parts`. let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr.into_parts() { (Some(alloc_id), offset) => { let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory(); @@ -164,6 +165,7 @@ pub(super) fn op_to_const<'tcx>( ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()), }, Immediate::ScalarPair(a, b) => { + // We know `offset` is relative to the allocation, so we can use `into_parts`. let (data, start) = match ecx.scalar_to_ptr(a.check_init().unwrap()).into_parts() { (Some(alloc_id), offset) => { (ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes()) diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index dcabc91f7a8e9..2bebbc65c24c2 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -168,11 +168,11 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { // Comparisons between integers are always known. (Scalar::Int { .. }, Scalar::Int { .. }) => a == b, // Equality with integers can never be known for sure. - (Scalar::Int { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Int { .. }) => false, + (Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => false, // FIXME: return `true` for when both sides are the same pointer, *except* that // some things (like functions and vtables) do not have stable addresses // so we need to be careful around them (see e.g. #73722). - (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + (Scalar::Ptr(..), Scalar::Ptr(..)) => false, } } @@ -183,13 +183,13 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { // Comparisons of abstract pointers with null pointers are known if the pointer // is in bounds, because if they are in bounds, the pointer can't be null. // Inequality with integers other than null can never be known for sure. - (Scalar::Int(int), Scalar::Ptr(ptr)) | (Scalar::Ptr(ptr), Scalar::Int(int)) => { + (Scalar::Int(int), Scalar::Ptr(ptr, _)) | (Scalar::Ptr(ptr, _), Scalar::Int(int)) => { int.is_null() && !self.memory.ptr_may_be_null(ptr.into()) } // FIXME: return `true` for at least some comparisons where we can reliably // determine the result of runtime inequality tests at compile-time. // Examples include comparison of addresses in different static items. - (Scalar::Ptr(_), Scalar::Ptr(_)) => false, + (Scalar::Ptr(..), Scalar::Ptr(..)) => false, } } } @@ -312,7 +312,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, align, interpret::MemoryKind::Machine(MemoryKind::Heap), )?; - ecx.write_scalar(Scalar::Ptr(ptr), dest)?; + ecx.write_scalar(Scalar::from_pointer(ptr, &*ecx.tcx), dest)?; } _ => { return Err(ConstEvalErrKind::NeedsRfc(format!( diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index 8254e5790013b..78124428787cd 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -35,7 +35,7 @@ pub(crate) fn const_caller_location( if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { bug!("intern_const_alloc_recursive should not error in this case") } - ConstValue::Scalar(Scalar::Ptr(loc_place.ptr.into_pointer_or_offset().unwrap())) + ConstValue::Scalar(Scalar::from_pointer(loc_place.ptr.into_pointer_or_offset().unwrap(), &tcx)) } /// Convert an evaluated constant to a type level constant diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 2821728b1d57a..ca7fd7010f3d1 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -57,7 +57,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_inval!(TooGeneric))?; let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(fn_ptr, dest)?; + self.write_scalar(Scalar::from_pointer(fn_ptr, &*self.tcx), dest)?; } _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), } @@ -88,7 +88,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ClosureKind::FnOnce, ); let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(fn_ptr, dest)?; + self.write_scalar(Scalar::from_pointer(fn_ptr, &*self.tcx), dest)?; } _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), } @@ -280,7 +280,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Initial cast from sized to dyn trait let vtable = self.get_vtable(src_pointee_ty, data.principal())?; let ptr = self.read_immediate(src)?.to_scalar()?; - let val = Immediate::new_dyn_trait(ptr, vtable); + let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx); self.write_immediate(val, dest) } diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index ef021435f46b5..71e4652ac0037 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -993,16 +993,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug } LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { write!(fmt, " {:?}", val)?; - if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val { allocs.push(ptr.provenance.erase_for_fmt()); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { write!(fmt, " ({:?}, {:?})", val1, val2)?; - if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val1 { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val1 { allocs.push(ptr.provenance.erase_for_fmt()); } - if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr)) = val2 { + if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val2 { allocs.push(ptr.provenance.erase_for_fmt()); } } diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 88dd94802d14f..4e2db2f13f177 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -362,9 +362,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // // Control flow is weird because we cannot early-return (to reach the // `go_to_block` at the end). - let done = if a.is_bits() && b.is_bits() { - let a = a.to_machine_usize(self)?; - let b = b.to_machine_usize(self)?; + let done = if let (Some(a), Some(b)) = (a.try_to_int(), b.try_to_int()) { + let a = a.try_to_machine_usize(*self.tcx).unwrap(); + let b = b.try_to_machine_usize(*self.tcx).unwrap(); if a == b && a != 0 { self.write_scalar(Scalar::from_machine_isize(0, self), dest)?; true diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index a9e1c605a1f91..c135e4f996342 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -283,6 +283,8 @@ pub trait Machine<'mir, 'tcx>: Sized { /// this will return an unusable tag (i.e., accesses will be UB)! /// /// Called on the id returned by `thread_local_static_alloc_id` and `extern_static_alloc_id`, if needed. + /// + /// `offset` is relative inside the allocation. fn tag_global_base_pointer( memory_extra: &Self::MemoryExtra, ptr: Pointer, @@ -485,6 +487,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { _mem: &Memory<$mir, $tcx, Self>, ptr: Pointer, ) -> (Option, Size) { + // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); (Some(alloc_id), offset) } diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 6a1d1bc3b2972..5e5a969c26ab8 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -165,6 +165,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &self, ptr: Pointer, ) -> InterpResult<'tcx, Pointer> { + // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); // We need to handle `extern static`. let alloc_id = match self.tcx.get_global_alloc(alloc_id) { @@ -450,7 +451,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // we want the error to be about the bounds. if let Some(align) = align { if M::force_int_for_alignment_check(&self.extra) { - let addr = Scalar::from(ptr) + let addr = Scalar::from_pointer(ptr, &self.tcx) .to_machine_usize(&self.tcx) .expect("ptr-to-int cast for align check should never fail"); check_offset_align(addr, align)?; @@ -1131,7 +1132,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn scalar_to_ptr(&self, scalar: Scalar) -> Pointer> { - match scalar.to_bits_or_ptr(self.pointer_size(), &self.tcx) { + match scalar.to_bits_or_ptr(self.pointer_size()) { Err(ptr) => ptr.into(), Ok(bits) => { let addr = u64::try_from(bits).unwrap(); diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index db054d1f279cd..e10f4fb7ff9ac 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -60,20 +60,21 @@ impl From> for Immediate { } } -impl From> for Immediate { - #[inline(always)] - fn from(val: Pointer) -> Self { - Immediate::Scalar(Scalar::from(val).into()) +impl<'tcx, Tag> Immediate { + pub fn from_pointer(p: Pointer, cx: &impl HasDataLayout) -> Self { + Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx)) + } + + pub fn from_maybe_pointer(p: Pointer>, cx: &impl HasDataLayout) -> Self { + Immediate::Scalar(ScalarMaybeUninit::from_maybe_pointer(p, cx)) } -} -impl<'tcx, Tag> Immediate { pub fn new_slice(val: Scalar, len: u64, cx: &impl HasDataLayout) -> Self { Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) } - pub fn new_dyn_trait(val: Scalar, vtable: Pointer) -> Self { - Immediate::ScalarPair(val.into(), vtable.into()) + pub fn new_dyn_trait(val: Scalar, vtable: Pointer, cx: &impl HasDataLayout) -> Self { + Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_pointer(vtable, cx)) } #[inline] @@ -252,7 +253,10 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { } #[inline] - pub fn to_const_int(self) -> ConstInt { + pub fn to_const_int(self) -> ConstInt + where + Tag: Provenance, + { assert!(self.layout.ty.is_integral()); let int = self.to_scalar().expect("to_const_int doesn't work on scalar pairs").assert_int(); ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) @@ -599,7 +603,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Other cases need layout. let tag_scalar = |scalar| -> InterpResult<'tcx, _> { Ok(match scalar { - Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), + Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), }) }; @@ -621,7 +625,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Size::from_bytes(start), // offset: `start` ); Operand::Immediate(Immediate::new_slice( - self.global_base_pointer(ptr)?.into(), + Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx), u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start` self, )) @@ -716,7 +720,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) { + let variant = match tag_val.to_bits_or_ptr(tag_layout.size) { Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index b5d26306d4be3..040262c5dc67c 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -752,7 +752,7 @@ where // This is a very common path, avoid some checks in release mode assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); match src { - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(_))) => assert_eq!( + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(..))) => assert_eq!( self.pointer_size(), dest.layout.size, "Size mismatch when writing pointer" diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 2fbcfcfbe9087..f0787ad0c660d 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -164,7 +164,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ThreadLocalRef(did) => { let id = M::thread_local_static_alloc_id(self, did)?; let val = self.global_base_pointer(id.into())?; - self.write_scalar(val, &dest)?; + self.write_scalar(Scalar::from_pointer(val, &*self.tcx), &dest)?; } Use(ref operand) => { diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 703c748024824..c633c87e19e41 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -539,7 +539,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // types below! if self.ctfe_mode.is_some() { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous - let is_bits = value.check_init().map_or(false, |v| v.is_bits()); + let is_bits = value.check_init().map_or(false, |v| v.try_to_int().is_some()); if !is_bits { throw_validation_failure!(self.path, { "{}", value } expected { "initialized plain (non-pointer) bytes" } @@ -657,7 +657,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' err_ub!(InvalidUninitBytes(None)) => { "{}", value } expected { "something {}", wrapping_range_format(valid_range, max_hi) }, ); - let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { + let bits = match value.to_bits_or_ptr(op.layout.size) { Err(ptr) => { if lo == 1 && hi == max_hi { // Only null is the niche. So make sure the ptr is NOT null. diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 548518a85e6fe..2ce7cf71116bb 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -1402,7 +1402,7 @@ fn collect_const_value<'tcx>( output: &mut Vec>>, ) { match value { - ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.provenance, output), + ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_miri(tcx, ptr.provenance, output), ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => { for &id in alloc.relocations().values() { collect_miri(tcx, id, output); diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index bbf3a1c669f3a..73bbb38a93a93 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -582,8 +582,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let left_size = self.ecx.layout_of(left_ty).ok()?.size; let right_size = r.layout.size; let r_bits = r.to_scalar().ok(); - // This is basically `force_bits`. - let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok()); + let r_bits = r_bits.and_then(|r| r.to_bits(right_size).ok()); if r_bits.map_or(false, |b| b >= left_size.bits() as u128) { debug!("check_binary_op: reporting assert for {:?}", source_info); self.report_assert_as_lint( @@ -922,12 +921,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { match **op { interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUninit::Scalar(s))) => { - s.is_bits() + s.try_to_int().is_some() } interpret::Operand::Immediate(Immediate::ScalarPair( ScalarMaybeUninit::Scalar(l), ScalarMaybeUninit::Scalar(r), - )) => l.is_bits() && r.is_bits(), + )) => l.try_to_int().is_some() && r.try_to_int().is_some(), _ => false, } } diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs index 0380218ec57f1..1ddf7c9cd0c9e 100644 --- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -46,7 +46,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { .expect("if we have an evaluated constant we must know the layout"); int.assert_bits(layout.size) } - Scalar::Ptr(_) => continue, + Scalar::Ptr(..) => continue, }; const FALSE: u128 = 0; diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index f31e2feac3118..d0b1bc47ea800 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -669,7 +669,7 @@ pub fn write_allocations<'tcx>( } fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator + '_ { match val { - ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { + ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _size)) => { Either::Left(Either::Left(std::iter::once(ptr.provenance))) } ConstValue::Scalar(interpret::Scalar::Int { .. }) => { diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index d62fd161e2f86..b3799aa063fa4 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -38,7 +38,7 @@ crate fn lit_to_const<'tcx>( } (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => { let id = tcx.allocate_bytes(data); - ConstValue::Scalar(Scalar::Ptr(id.into())) + ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx)) } (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index da8cd66acb175..c3908ddd4fbe8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -928,7 +928,11 @@ impl<'tcx> Cx<'tcx> { } else { let ptr = self.tcx.create_static_alloc(id); ExprKind::StaticRef { - literal: ty::Const::from_scalar(self.tcx, Scalar::Ptr(ptr.into()), ty), + literal: ty::Const::from_scalar( + self.tcx, + Scalar::from_pointer(ptr.into(), &self.tcx), + ty, + ), def_id: id, } }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 4b5b648c5044f..db15f6dad3c48 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -123,7 +123,7 @@ impl IntRange { // straight to the result, after doing a bit of checking. (We // could remove this branch and just fall through, which // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr(target_size, &tcx) { + if let Ok(bits) = scalar.to_bits_or_ptr(target_size) { return Some(bits); } } From 71c166a0dcd0389f943284ed358a3dbee6a0e4a8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 13 Jul 2021 08:58:59 +0200 Subject: [PATCH 03/16] use NonZeroU64 for AllocId to restore old type sizes --- compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/mir/interpret/mod.rs | 6 +++--- compiler/rustc_middle/src/mir/interpret/pointer.rs | 2 +- compiler/rustc_middle/src/mir/interpret/value.rs | 4 ++-- compiler/rustc_mir/src/interpret/operand.rs | 4 ++-- compiler/rustc_mir/src/interpret/place.rs | 10 +++++----- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 57f507290e894..f2acc601d4f62 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -49,6 +49,7 @@ #![feature(iter_zip)] #![feature(thread_local_const_init)] #![feature(try_reserve)] +#![feature(nonzero_ops)] #![recursion_limit = "512"] #[macro_use] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 44fa94c89c5cd..86bd35003bb1a 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -99,7 +99,7 @@ use std::convert::TryFrom; use std::fmt; use std::io; use std::io::{Read, Write}; -use std::num::NonZeroU32; +use std::num::{NonZeroU32, NonZeroU64}; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_ast::LitKind; @@ -177,7 +177,7 @@ pub enum LitToConstError { } #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct AllocId(pub u64); +pub struct AllocId(pub NonZeroU64); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. @@ -428,7 +428,7 @@ crate struct AllocMap<'tcx> { impl<'tcx> AllocMap<'tcx> { crate fn new() -> Self { - AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } + AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(NonZeroU64::new(1).unwrap()) } } fn reserve(&mut self) -> AllocId { let next = self.next_id; diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 3d6ee49a19e0f..a95e39e181130 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -135,7 +135,7 @@ pub struct Pointer { pub provenance: Tag, } -//FIXME static_assert_size!(Pointer, 16); +static_assert_size!(Pointer, 16); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 831a4d33ca20b..bb6f1bb21c623 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -136,7 +136,7 @@ pub enum Scalar { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME static_assert_size!(Scalar, 24); +static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. @@ -522,7 +522,7 @@ pub enum ScalarMaybeUninit { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME static_assert_size!(ScalarMaybeUninit, 24); +static_assert_size!(ScalarMaybeUninit, 24); impl From> for ScalarMaybeUninit { #[inline(always)] diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index e10f4fb7ff9ac..fefda32bfe638 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -34,7 +34,7 @@ pub enum Immediate { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(Immediate, 56); +rustc_data_structures::static_assert_size!(Immediate, 56); impl std::fmt::Debug for Immediate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -100,7 +100,7 @@ pub struct ImmTy<'tcx, Tag = AllocId> { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); +rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); impl<'tcx, Tag: Provenance> std::fmt::Debug for ImmTy<'tcx, Tag> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 040262c5dc67c..5e35a83a5b9b7 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -34,7 +34,7 @@ pub enum MemPlaceMeta { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); +rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); impl std::fmt::Debug for MemPlaceMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -87,7 +87,7 @@ pub struct MemPlace { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(MemPlace, 56); +rustc_data_structures::static_assert_size!(MemPlace, 48); impl std::fmt::Debug for MemPlace { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -111,7 +111,7 @@ pub enum Place { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(Place, 64); +rustc_data_structures::static_assert_size!(Place, 56); impl std::fmt::Debug for Place { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -132,7 +132,7 @@ pub struct PlaceTy<'tcx, Tag = AllocId> { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(PlaceTy<'_>, 80); +rustc_data_structures::static_assert_size!(PlaceTy<'_>, 72); impl<'tcx, Tag: Provenance> std::fmt::Debug for PlaceTy<'tcx, Tag> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -157,7 +157,7 @@ pub struct MPlaceTy<'tcx, Tag = AllocId> { } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -//FIXME rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 72); +rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64); impl<'tcx, Tag: Provenance> std::fmt::Debug for MPlaceTy<'tcx, Tag> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { From 172abb29b1a450f8dc8ca7d7c05d9a9f21dc18cb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 13 Jul 2021 00:41:10 +0200 Subject: [PATCH 04/16] fix cranelift --- .../rustc_codegen_cranelift/src/constant.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 2a2573aad2950..c87309e22224f 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -193,20 +193,21 @@ pub(crate) fn codegen_const_value<'tcx>( place.to_cvalue(fx) } } - Scalar::Ptr(ptr) => { - let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id); + Scalar::Ptr(ptr, _size) => { + let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative + let alloc_kind = fx.tcx.get_global_alloc(alloc_id); let base_addr = match alloc_kind { Some(GlobalAlloc::Memory(alloc)) => { let data_id = data_id_for_alloc_id( &mut fx.constants_cx, fx.module, - ptr.alloc_id, + alloc_id, alloc.mutability, ); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); if fx.clif_comments.enabled() { - fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id)); + fx.add_comment(local_data_id, format!("{:?}", alloc_id)); } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } @@ -226,10 +227,10 @@ pub(crate) fn codegen_const_value<'tcx>( } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - None => bug!("missing allocation {:?}", ptr.alloc_id), + None => bug!("missing allocation {:?}", alloc_id), }; - let val = if ptr.offset.bytes() != 0 { - fx.bcx.ins().iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap()) + let val = if offset.bytes() != 0 { + fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap()) } else { base_addr }; @@ -406,7 +407,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); data_ctx.define(bytes.into_boxed_slice()); - for &(offset, (_tag, reloc)) in alloc.relocations().iter() { + for &(offset, alloc_id) in alloc.relocations().iter() { let addend = { let endianness = tcx.data_layout.endian; let offset = offset.bytes() as usize; @@ -417,7 +418,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant read_target_uint(endianness, bytes).unwrap() }; - let reloc_target_alloc = tcx.get_global_alloc(reloc).unwrap(); + let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap(); let data_id = match reloc_target_alloc { GlobalAlloc::Function(instance) => { assert_eq!(addend, 0); @@ -427,7 +428,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant continue; } GlobalAlloc::Memory(target_alloc) => { - data_id_for_alloc_id(cx, module, reloc, target_alloc.mutability) + data_id_for_alloc_id(cx, module, alloc_id, target_alloc.mutability) } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) From 4759090d15b176f4a2501c512de776be46f37747 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 13 Jul 2021 00:21:35 +0200 Subject: [PATCH 05/16] adjust tests --- .../ui/const-ptr/out_of_bounds_read.stderr | 6 +- ...inter-values-in-various-types.64bit.stderr | 28 ++++---- .../consts/const-eval/const_raw_ptr_ops2.rs | 8 +-- .../const-eval/const_raw_ptr_ops2.stderr | 20 ++---- .../heap/alloc_intrinsic_uninit.32bit.stderr | 2 +- .../heap/alloc_intrinsic_uninit.64bit.stderr | 2 +- .../ui/consts/const-eval/issue-49296.stderr | 2 +- .../const-eval/ref_to_int_match.32bit.stderr | 4 +- .../const-eval/ref_to_int_match.64bit.stderr | 4 +- .../ui/consts/const-eval/ub-enum.32bit.stderr | 20 +++--- .../ui/consts/const-eval/ub-enum.64bit.stderr | 20 +++--- .../consts/const-eval/ub-nonnull.32bit.stderr | 2 +- .../consts/const-eval/ub-nonnull.64bit.stderr | 2 +- .../consts/const-eval/ub-ref-ptr.32bit.stderr | 16 ++--- .../consts/const-eval/ub-ref-ptr.64bit.stderr | 16 ++--- .../consts/const-eval/ub-upvars.32bit.stderr | 2 +- .../consts/const-eval/ub-upvars.64bit.stderr | 2 +- .../const-eval/ub-wide-ptr.32bit.stderr | 2 +- .../const-eval/ub-wide-ptr.64bit.stderr | 2 +- .../const-mut-refs/issue-76510.32bit.stderr | 2 +- .../const-mut-refs/issue-76510.64bit.stderr | 2 +- .../mut_ref_in_final_dynamic_check.rs | 4 +- .../mut_ref_in_final_dynamic_check.stderr | 12 ++-- .../const-points-to-static.32bit.stderr | 2 +- .../const-points-to-static.64bit.stderr | 2 +- src/test/ui/consts/copy-intrinsic.stderr | 4 +- src/test/ui/consts/issue-63952.32bit.stderr | 2 +- src/test/ui/consts/issue-63952.64bit.stderr | 2 +- src/test/ui/consts/issue-79690.64bit.stderr | 2 +- src/test/ui/consts/issue-83182.32bit.stderr | 2 +- src/test/ui/consts/issue-83182.64bit.stderr | 2 +- .../const_refers_to_static2.32bit.stderr | 4 +- .../const_refers_to_static2.64bit.stderr | 4 +- ..._refers_to_static_cross_crate.32bit.stderr | 4 +- ..._refers_to_static_cross_crate.64bit.stderr | 4 +- .../mutable_references_err.32bit.stderr | 6 +- .../mutable_references_err.64bit.stderr | 6 +- .../ui/consts/miri_unleashed/ptr_arith.rs | 4 +- .../ui/consts/miri_unleashed/ptr_arith.stderr | 2 +- src/test/ui/consts/offset_from_ub.rs | 4 +- src/test/ui/consts/offset_from_ub.stderr | 40 ++++------- src/test/ui/consts/offset_ub.rs | 2 + src/test/ui/consts/offset_ub.stderr | 67 ++++++++----------- src/test/ui/consts/ptr_comparisons.rs | 8 +-- src/test/ui/consts/ptr_comparisons.stderr | 8 +-- .../intrinsic-raw_eq-const-padding.stderr | 2 +- 46 files changed, 163 insertions(+), 200 deletions(-) diff --git a/src/test/ui/const-ptr/out_of_bounds_read.stderr b/src/test/ui/const-ptr/out_of_bounds_read.stderr index 67175d5d1f5c3..e6e9f483c2b3c 100644 --- a/src/test/ui/const-ptr/out_of_bounds_read.stderr +++ b/src/test/ui/const-ptr/out_of_bounds_read.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL @@ -23,7 +23,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL @@ -47,7 +47,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds at offset 8, but is outside bounds of alloc6 which has size 4 + | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL diff --git a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr index 187e2760ad2ca..5f23cc2311db0 100644 --- a/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr +++ b/src/test/ui/consts/const-eval/const-pointer-values-in-various-types.64bit.stderr @@ -2,11 +2,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:26:5 | LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc3, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc2────────╼ │ ╾──────╼ + ╾───────alloc3────────╼ │ ╾──────╼ } error: any use of this value will cause an error @@ -47,11 +47,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:41:5 | LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc18, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc19, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc18───────╼ │ ╾──────╼ + ╾───────alloc19───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -102,11 +102,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:59:5 | LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc38, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc39, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc38───────╼ │ ╾──────╼ + ╾───────alloc39───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -135,11 +135,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:69:5 | LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc50, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc51, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc50───────╼ │ ╾──────╼ + ╾───────alloc51───────╼ │ ╾──────╼ } error: any use of this value will cause an error @@ -201,11 +201,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:92:5 | LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc71, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc72, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc71───────╼ │ ╾──────╼ + ╾───────alloc72───────╼ │ ╾──────╼ } error: any use of this value will cause an error @@ -256,11 +256,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:111:5 | LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc86, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc87, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc86───────╼ │ ╾──────╼ + ╾───────alloc87───────╼ │ ╾──────╼ } error: any use of this value will cause an error @@ -289,11 +289,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/const-pointer-values-in-various-types.rs:122:5 | LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc95, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc96, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc95───────╼ │ ╾──────╼ + ╾───────alloc96───────╼ │ ╾──────╼ } error: any use of this value will cause an error diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs index a177ed6b3413e..11f6a58be360c 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs @@ -6,7 +6,7 @@ fn main() {} const Z: i32 = unsafe { *(&1 as *const i32) }; // bad, will thus error in miri -const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR any use of this value will cause -//~| WARN this was previously accepted by the compiler but is being phased out -const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR any use of this value will cause -//~| WARN this was previously accepted by the compiler but is being phased out +const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR evaluation of constant value failed +//~| is not a valid pointer +const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR evaluation of constant value failed +//~| is not a valid pointer diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr index de54ea3b9fbd0..ca3290077378a 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr @@ -1,25 +1,15 @@ -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:9:26 | LL | const Z2: i32 = unsafe { *(42 as *const i32) }; - | -------------------------^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn bytes into a pointer - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | ^^^^^^^^^^^^^^^^^^^ 0x2a is not a valid pointer -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/const_raw_ptr_ops2.rs:11:26 | LL | const Z3: i32 = unsafe { *(44 as *const i32) }; - | -------------------------^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn bytes into a pointer - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | ^^^^^^^^^^^^^^^^^^^ 0x2c is not a valid pointer error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr index 23b847b8f3aed..c11fea8533d85 100644 --- a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr +++ b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.32bit.stderr @@ -6,7 +6,7 @@ LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) | = 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) { - ╾─alloc1──╼ │ ╾──╼ + ╾─alloc2──╼ │ ╾──╼ } error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr index ff21a31c9c23f..58d24dc2e8e0b 100644 --- a/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr +++ b/src/test/ui/consts/const-eval/heap/alloc_intrinsic_uninit.64bit.stderr @@ -6,7 +6,7 @@ LL | const BAR: &i32 = unsafe { &*(intrinsics::const_allocate(4, 4) as *mut i32) | = 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) { - ╾───────alloc1────────╼ │ ╾──────╼ + ╾───────alloc2────────╼ │ ╾──────╼ } error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/issue-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index 49ec9eb047be4..4fd8e18909655 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/issue-49296.rs:19:16 | LL | const X: u64 = *wat(42); - | ^^^^^^^^ pointer to alloc1 was dereferenced after this allocation got freed + | ^^^^^^^^ pointer to alloc2 was dereferenced after this allocation got freed error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr index c14457490ac45..ff90bb5fdd242 100644 --- a/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr +++ b/src/test/ui/consts/const-eval/ref_to_int_match.32bit.stderr @@ -2,11 +2,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ref_to_int_match.rs:26:1 | LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc3, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc2──╼ │ ╾──╼ + ╾─alloc3──╼ │ ╾──╼ } error: could not evaluate constant pattern diff --git a/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr b/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr index 6ea1cf145a26c..499c4536c4b66 100644 --- a/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr +++ b/src/test/ui/consts/const-eval/ref_to_int_match.64bit.stderr @@ -2,11 +2,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ref_to_int_match.rs:26:1 | LL | const BAR: Int = unsafe { Foo { r: &42 }.f }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc2, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc3, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc2────────╼ │ ╾──────╼ + ╾───────alloc3────────╼ │ ╾──────╼ } error: could not evaluate constant pattern diff --git a/src/test/ui/consts/const-eval/ub-enum.32bit.stderr b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr index 4abc6a479a3d4..850acb52b0c89 100644 --- a/src/test/ui/consts/const-eval/ub-enum.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.32bit.stderr @@ -13,22 +13,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc8, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc9, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc8──╼ │ ╾──╼ + ╾─alloc9──╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:30:1 | LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc12, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc13, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc12─╼ │ ╾──╼ + ╾─alloc13─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -46,22 +46,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:44:1 | LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc18, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc19, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc18─╼ │ ╾──╼ + ╾─alloc19─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:47:1 | LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc22, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc23, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc22─╼ │ ╾──╼ + ╾─alloc23─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -79,11 +79,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:60:1 | LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc28, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc29, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc28─╼ │ ╾──╼ + ╾─alloc29─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value diff --git a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr index 1716f5b05b76e..4f7dd5cdf7c73 100644 --- a/src/test/ui/consts/const-eval/ub-enum.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-enum.64bit.stderr @@ -13,22 +13,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc8, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc9, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc8────────╼ │ ╾──────╼ + ╾───────alloc9────────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:30:1 | LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc12, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc13, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc12───────╼ │ ╾──────╼ + ╾───────alloc13───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -46,22 +46,22 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:44:1 | LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc18, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc19, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc18───────╼ │ ╾──────╼ + ╾───────alloc19───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:47:1 | LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc22, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .0.: encountered pointer to alloc23, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc22───────╼ │ ╾──────╼ + ╾───────alloc23───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -79,11 +79,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:60:1 | LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc28, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at .: encountered pointer to alloc29, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc28───────╼ │ ╾──────╼ + ╾───────alloc29───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value diff --git a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr index 9d3b88e803ebd..a0f69d5d4cc0c 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1 + | ^^^^^^^^ memory access failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr index bc230374ebf84..d1c2025821c54 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ memory access failed: pointer must be in-bounds at offset 256, but is outside bounds of alloc10 which has size 1 + | ^^^^^^^^ memory access failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr index bae44a42464a5..c8ec7cc4875a5 100644 --- a/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr @@ -6,7 +6,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | = 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) { - ╾─alloc2──╼ │ ╾──╼ + ╾─alloc3──╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -17,7 +17,7 @@ LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | = 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) { - ╾─alloc6──╼ │ ╾──╼ + ╾─alloc7──╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -46,11 +46,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:30:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc14, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc15, but expected initialized plain (non-pointer) bytes | = 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) { - ╾─alloc14─╼ │ ╾──╼ + ╾─alloc15─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -61,7 +61,7 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | = 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) { - ╾─alloc20─╼ │ ╾──╼ + ╾─alloc21─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -72,14 +72,14 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us | = 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) { - ╾─alloc25─╼ │ ╾──╼ + ╾─alloc26─╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:39:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (address 0x539 is unallocated) | = 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) { @@ -90,7 +90,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:42:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (address 0x539 is unallocated) | = 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/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr index 697ff0a55eced..60a174ac5a7f6 100644 --- a/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr @@ -6,7 +6,7 @@ LL | const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) }; | = 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) { - ╾───────alloc2────────╼ │ ╾──────╼ + ╾───────alloc3────────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -17,7 +17,7 @@ LL | const UNALIGNED_BOX: Box = unsafe { mem::transmute(&[0u8; 4]) }; | = 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) { - ╾───────alloc6────────╼ │ ╾──────╼ + ╾───────alloc7────────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -46,11 +46,11 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:30:1 | LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc14, but expected initialized plain (non-pointer) bytes + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered pointer to alloc15, but expected initialized plain (non-pointer) bytes | = 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) { - ╾───────alloc14───────╼ │ ╾──────╼ + ╾───────alloc15───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -61,7 +61,7 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }]; | = 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) { - ╾───────alloc20───────╼ │ ╾──────╼ + ╾───────alloc21───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -72,14 +72,14 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us | = 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) { - ╾───────alloc25───────╼ │ ╾──────╼ + ╾───────alloc26───────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:39:1 | LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (created from integer) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (address 0x539 is unallocated) | = 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) { @@ -90,7 +90,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-ref-ptr.rs:42:1 | LL | const USIZE_AS_BOX: Box = unsafe { mem::transmute(1337usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (created from integer) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (address 0x539 is unallocated) | = 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/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr b/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr index 0818d7fbf88bc..27b98c2547ce7 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-upvars.32bit.stderr @@ -10,7 +10,7 @@ LL | | }; | = 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) { - ╾─alloc2──╼ ╾─alloc5──╼ │ ╾──╼╾──╼ + ╾─alloc3──╼ ╾─alloc6──╼ │ ╾──╼╾──╼ } error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr b/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr index 59dcf7aefe41f..9cc9fb59bd7f0 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-upvars.64bit.stderr @@ -10,7 +10,7 @@ LL | | }; | = 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) { - ╾───────alloc2────────╼ ╾───────alloc5────────╼ │ ╾──────╼╾──────╼ + ╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼ } error: aborting due to previous error diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr index f0934418a9b0e..6609265e706b3 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr @@ -302,7 +302,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/ub-wide-ptr.rs:139:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 12 bytes at offset N, but allocN has size N error: aborting due to 28 previous errors diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr index 0bb8c17ac82fe..fc9f02400113f 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr @@ -302,7 +302,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/ub-wide-ptr.rs:139:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 24 bytes at offset N, but allocN has size N error: aborting due to 28 previous errors diff --git a/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr b/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr index 965bc67a381ae..a9411fb0e3d4c 100644 --- a/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr +++ b/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr @@ -27,7 +27,7 @@ LL | const S: &'static mut str = &mut " hello "; | = 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) { - ╾─alloc2──╼ 07 00 00 00 │ ╾──╼.... + ╾─alloc3──╼ 07 00 00 00 │ ╾──╼.... } error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr b/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr index ac7d5993585e8..9ad5f20d57cb2 100644 --- a/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr +++ b/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr @@ -27,7 +27,7 @@ LL | const S: &'static mut str = &mut " hello "; | = 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) { - ╾───────alloc2────────╼ 07 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾───────alloc3────────╼ 07 00 00 00 00 00 00 00 │ ╾──────╼........ } error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs b/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs index 638a98130a236..24d7bc49147d8 100644 --- a/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs +++ b/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs @@ -11,8 +11,8 @@ const fn helper() -> Option<&'static mut i32> { unsafe { // Undefined behaviour (integer as pointer), who doesn't love tests like this. // This code never gets executed, because the static checks fail before that. - Some(&mut *(42 as *mut i32)) //~ ERROR any use of this value will cause an error - //~| WARN this was previously accepted by the compiler but is being phased out + Some(&mut *(42 as *mut i32)) //~ ERROR evaluation of constant value failed + //~| 0x2a is not a valid pointer } } // The error is an evaluation error and not a validation error, so the error is reported // directly at the site where it occurs. diff --git a/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr b/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr index 6d3d18f6e6898..b3fcd6a7fecd3 100644 --- a/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr +++ b/src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr @@ -1,19 +1,14 @@ -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/mut_ref_in_final_dynamic_check.rs:14:10 | LL | Some(&mut *(42 as *mut i32)) | ^^^^^^^^^^^^^^^^^^^^^^ | | - | unable to turn bytes into a pointer + | 0x2a is not a valid pointer | inside `helper` at $DIR/mut_ref_in_final_dynamic_check.rs:14:10 - | inside `A` at $DIR/mut_ref_in_final_dynamic_check.rs:19:29 ... LL | const A: Option<&mut i32> = helper(); - | ------------------------------------- - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | -------- inside `A` at $DIR/mut_ref_in_final_dynamic_check.rs:19:29 error: encountered dangling pointer in final constant --> $DIR/mut_ref_in_final_dynamic_check.rs:26:1 @@ -23,3 +18,4 @@ LL | const B: Option<&mut i32> = helper2(); error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-points-to-static.32bit.stderr b/src/test/ui/consts/const-points-to-static.32bit.stderr index c582678e2d65b..78af2c30d0437 100644 --- a/src/test/ui/consts/const-points-to-static.32bit.stderr +++ b/src/test/ui/consts/const-points-to-static.32bit.stderr @@ -6,7 +6,7 @@ LL | const TEST: &u8 = &MY_STATIC; | = 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) { - ╾─alloc0──╼ │ ╾──╼ + ╾─alloc1──╼ │ ╾──╼ } warning: skipping const checks diff --git a/src/test/ui/consts/const-points-to-static.64bit.stderr b/src/test/ui/consts/const-points-to-static.64bit.stderr index 1112499a3ee64..f5a0a6b0addef 100644 --- a/src/test/ui/consts/const-points-to-static.64bit.stderr +++ b/src/test/ui/consts/const-points-to-static.64bit.stderr @@ -6,7 +6,7 @@ LL | const TEST: &u8 = &MY_STATIC; | = 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) { - ╾───────alloc0────────╼ │ ╾──────╼ + ╾───────alloc1────────╼ │ ╾──────╼ } warning: skipping const checks diff --git a/src/test/ui/consts/copy-intrinsic.stderr b/src/test/ui/consts/copy-intrinsic.stderr index c1779743e8e00..4e35a8fde940c 100644 --- a/src/test/ui/consts/copy-intrinsic.stderr +++ b/src/test/ui/consts/copy-intrinsic.stderr @@ -2,13 +2,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:27:5 | LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 40, but is outside bounds of alloc4 which has size 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 0 bytes at offset 40, but alloc5 has size 4 error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:33:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 40, but is outside bounds of alloc6 which has size 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 0 bytes at offset 40, but alloc7 has size 4 error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:40:5 diff --git a/src/test/ui/consts/issue-63952.32bit.stderr b/src/test/ui/consts/issue-63952.32bit.stderr index 6d52ed065bff2..6a6097d5ec623 100644 --- a/src/test/ui/consts/issue-63952.32bit.stderr +++ b/src/test/ui/consts/issue-63952.32bit.stderr @@ -12,7 +12,7 @@ LL | | }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─alloc3──╼ ff ff ff ff │ ╾──╼.... + ╾─alloc4──╼ ff ff ff ff │ ╾──╼.... } error: aborting due to previous error diff --git a/src/test/ui/consts/issue-63952.64bit.stderr b/src/test/ui/consts/issue-63952.64bit.stderr index 3335c5bf72ef5..6547c3384da82 100644 --- a/src/test/ui/consts/issue-63952.64bit.stderr +++ b/src/test/ui/consts/issue-63952.64bit.stderr @@ -12,7 +12,7 @@ LL | | }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────alloc3────────╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ + ╾───────alloc4────────╼ ff ff ff ff ff ff ff ff │ ╾──────╼........ } error: aborting due to previous error diff --git a/src/test/ui/consts/issue-79690.64bit.stderr b/src/test/ui/consts/issue-79690.64bit.stderr index cab111ab69cdc..bf0ef9e6dc07e 100644 --- a/src/test/ui/consts/issue-79690.64bit.stderr +++ b/src/test/ui/consts/issue-79690.64bit.stderr @@ -6,7 +6,7 @@ LL | const G: Fat = unsafe { Transmute { t: FOO }.u }; | = 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) { - ╾───────alloc2────────╼ ╾───────alloc5────────╼ │ ╾──────╼╾──────╼ + ╾───────alloc3────────╼ ╾───────alloc6────────╼ │ ╾──────╼╾──────╼ } error: aborting due to previous error diff --git a/src/test/ui/consts/issue-83182.32bit.stderr b/src/test/ui/consts/issue-83182.32bit.stderr index 57f1e43d88c71..a15668d4ec1b9 100644 --- a/src/test/ui/consts/issue-83182.32bit.stderr +++ b/src/test/ui/consts/issue-83182.32bit.stderr @@ -6,7 +6,7 @@ LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─alloc3──╼ 01 00 00 00 │ ╾──╼.... + ╾─alloc4──╼ 01 00 00 00 │ ╾──╼.... } error: aborting due to previous error diff --git a/src/test/ui/consts/issue-83182.64bit.stderr b/src/test/ui/consts/issue-83182.64bit.stderr index f5aaec91b013f..5c3ade7c1de44 100644 --- a/src/test/ui/consts/issue-83182.64bit.stderr +++ b/src/test/ui/consts/issue-83182.64bit.stderr @@ -6,7 +6,7 @@ LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[&()]) }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────alloc3────────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾───────alloc4────────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error: aborting due to previous error diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr index 64ebd01226731..1a6ed0f43b0f1 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.32bit.stderr @@ -10,7 +10,7 @@ LL | | }; | = 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) { - ╾─alloc0──╼ │ ╾──╼ + ╾─alloc1──╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -25,7 +25,7 @@ LL | | }; | = 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) { - ╾─alloc1──╼ │ ╾──╼ + ╾─alloc2──╼ │ ╾──╼ } warning: skipping const checks diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr index 4157b313f9f55..4f268dd905d03 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static2.64bit.stderr @@ -10,7 +10,7 @@ LL | | }; | = 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) { - ╾───────alloc0────────╼ │ ╾──────╼ + ╾───────alloc1────────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -25,7 +25,7 @@ LL | | }; | = 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) { - ╾───────alloc1────────╼ │ ╾──────╼ + ╾───────alloc2────────╼ │ ╾──────╼ } warning: skipping const checks diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr index 583b2ef672510..64a1214e8bf1b 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.32bit.stderr @@ -9,7 +9,7 @@ LL | | }; | = 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) { - ╾─alloc0──╼ │ ╾──╼ + ╾─alloc1──╼ │ ╾──╼ } error: could not evaluate constant pattern @@ -29,7 +29,7 @@ LL | | }; | = 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) { - ╾─alloc0──╼ │ ╾──╼ + ╾─alloc1──╼ │ ╾──╼ } error: could not evaluate constant pattern diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr index b381a25a63df2..66fe4ec076005 100644 --- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr +++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.64bit.stderr @@ -9,7 +9,7 @@ LL | | }; | = 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) { - ╾───────alloc0────────╼ │ ╾──────╼ + ╾───────alloc1────────╼ │ ╾──────╼ } error: could not evaluate constant pattern @@ -29,7 +29,7 @@ LL | | }; | = 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) { - ╾───────alloc0────────╼ │ ╾──────╼ + ╾───────alloc1────────╼ │ ╾──────╼ } error: could not evaluate constant pattern diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr index e6db2eef955e0..9d1bb2ac91c49 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr @@ -8,7 +8,7 @@ LL | | }; | = 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) { - ╾─alloc2──╼ │ ╾──╼ + ╾─alloc3──╼ │ ╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -19,7 +19,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─alloc6──╼ ╾─alloc8──╼ │ ╾──╼╾──╼ + ╾─alloc7──╼ ╾─alloc9──╼ │ ╾──╼╾──╼ } error[E0080]: it is undefined behavior to use this value @@ -30,7 +30,7 @@ LL | const BLUNT: &mut i32 = &mut 42; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 4, align: 4) { - ╾─alloc10─╼ │ ╾──╼ + ╾─alloc11─╼ │ ╾──╼ } warning: skipping const checks diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr index f48978d4af6b0..fe939c0393189 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr @@ -8,7 +8,7 @@ LL | | }; | = 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) { - ╾───────alloc2────────╼ │ ╾──────╼ + ╾───────alloc3────────╼ │ ╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -19,7 +19,7 @@ LL | const SNEAKY: &dyn Sync = &Synced { x: UnsafeCell::new(42) }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────alloc6────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼ + ╾───────alloc7────────╼ ╾───────alloc9────────╼ │ ╾──────╼╾──────╼ } error[E0080]: it is undefined behavior to use this value @@ -30,7 +30,7 @@ LL | const BLUNT: &mut i32 = &mut 42; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 8) { - ╾───────alloc10───────╼ │ ╾──────╼ + ╾───────alloc11───────╼ │ ╾──────╼ } warning: skipping const checks diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index aae6d837204e3..902d67c126109 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -8,14 +8,14 @@ static CMP: () = { let x = &0 as *const _; let _v = x == x; //~^ ERROR could not evaluate static initializer - //~| NOTE pointer arithmetic or comparison + //~| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants }; static INT_PTR_ARITH: () = unsafe { let x: usize = std::mem::transmute(&0); let _v = x + 0; //~^ ERROR could not evaluate static initializer - //~| NOTE cannot cast pointer to integer + //~| unable to turn pointer into raw bytes }; fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index 8ac4aa87f642a..bd574a53a6082 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:16:14 | LL | let _v = x + 0; - | ^^^^^ cannot cast pointer to integer because it was not created by cast from integer + | ^^^^^ unable to turn pointer into raw bytes warning: skipping const checks | diff --git a/src/test/ui/consts/offset_from_ub.rs b/src/test/ui/consts/offset_from_ub.rs index f8fa2c5f177ea..4b55c299b5c4c 100644 --- a/src/test/ui/consts/offset_from_ub.rs +++ b/src/test/ui/consts/offset_from_ub.rs @@ -41,8 +41,8 @@ pub const OFFSET_FROM_NULL: isize = { pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; - unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR any use of this value will cause an error - //~| WARN previously accepted + unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed + //~| 0x10 is not a valid pointer }; fn main() {} diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr index 4c2ba9297d84a..663bfbdade0ce 100644 --- a/src/test/ui/consts/offset_from_ub.stderr +++ b/src/test/ui/consts/offset_from_ub.stderr @@ -4,26 +4,19 @@ error[E0080]: evaluation of constant value failed LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from cannot compute offset of pointers into different allocations. -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | -LL | unsafe { intrinsics::ptr_offset_from(self, origin) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | unable to turn bytes into a pointer - | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `NOT_PTR` at $DIR/offset_from_ub.rs:24:14 +LL | unsafe { intrinsics::ptr_offset_from(self, origin) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | 0x2a is not a valid pointer + | inside `ptr::const_ptr::::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_from_ub.rs:23:1 - | -LL | / pub const NOT_PTR: usize = { -LL | | unsafe { (42 as *const u8).offset_from(&5u8) as usize } -LL | | }; - | |__- + ::: $DIR/offset_from_ub.rs:24:14 | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 +LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize } + | ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:24:14 error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:31:14 @@ -37,20 +30,11 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(ptr, ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^ null pointer is not a valid pointer for this operation -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:44:14 | -LL | / pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC -LL | | let ptr1 = 8 as *const u8; -LL | | let ptr2 = 16 as *const u8; -LL | | unsafe { ptr_offset_from(ptr2, ptr1) } - | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn bytes into a pointer -LL | | -LL | | }; - | |__- - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 +LL | unsafe { ptr_offset_from(ptr2, ptr1) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0x10 is not a valid pointer error: aborting due to 5 previous errors diff --git a/src/test/ui/consts/offset_ub.rs b/src/test/ui/consts/offset_ub.rs index 7ce45ba9c4b2b..a22296a7b0085 100644 --- a/src/test/ui/consts/offset_ub.rs +++ b/src/test/ui/consts/offset_ub.rs @@ -2,6 +2,8 @@ use std::ptr; // normalize-stderr-test "alloc\d+" -> "allocN" +// normalize-stderr-test "0x7f+" -> "0x7f..f" + pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; //~NOTE pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; //~NOTE diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index e25bef0624de0..edd5f021d08e5 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -7,10 +7,10 @@ LL | unsafe { intrinsics::offset(self, count) } | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:6:46 + ::: $DIR/offset_ub.rs:8:46 | LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; - | ------------------------------ inside `BEFORE_START` at $DIR/offset_ub.rs:6:46 + | ------------------------------ inside `BEFORE_START` at $DIR/offset_ub.rs:8:46 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -18,13 +18,13 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 + | pointer arithmetic failed: pointer must be in-bounds for 2 bytes at offset 0, but allocN has size 1 | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:7:43 + ::: $DIR/offset_ub.rs:9:43 | LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; - | ----------------------------- inside `AFTER_END` at $DIR/offset_ub.rs:7:43 + | ----------------------------- inside `AFTER_END` at $DIR/offset_ub.rs:9:43 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -32,13 +32,13 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 + | pointer arithmetic failed: pointer must be in-bounds for 101 bytes at offset 0, but allocN has size 100 | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:8:45 + ::: $DIR/offset_ub.rs:10:45 | LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; - | ------------------------------- inside `AFTER_ARRAY` at $DIR/offset_ub.rs:8:45 + | ------------------------------- inside `AFTER_ARRAY` at $DIR/offset_ub.rs:10:45 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -49,10 +49,10 @@ LL | unsafe { intrinsics::offset(self, count) } | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:10:43 + ::: $DIR/offset_ub.rs:12:43 | LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; - | ------------------------------------- inside `OVERFLOW` at $DIR/offset_ub.rs:10:43 + | ------------------------------------- inside `OVERFLOW` at $DIR/offset_ub.rs:12:43 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -63,10 +63,10 @@ LL | unsafe { intrinsics::offset(self, count) } | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:11:44 + ::: $DIR/offset_ub.rs:13:44 | LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; - | ------------------------------------- inside `UNDERFLOW` at $DIR/offset_ub.rs:11:44 + | ------------------------------------- inside `UNDERFLOW` at $DIR/offset_ub.rs:13:44 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -77,10 +77,10 @@ LL | unsafe { intrinsics::offset(self, count) } | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:12:56 + ::: $DIR/offset_ub.rs:14:56 | LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; - | ----------------------------------- inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:12:56 + | ----------------------------------- inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:56 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -91,10 +91,10 @@ LL | unsafe { intrinsics::offset(self, count) } | overflowing in-bounds pointer arithmetic | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:13:57 + ::: $DIR/offset_ub.rs:15:57 | LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; - | --------------------------- inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:57 + | --------------------------- inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:15:57 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -102,32 +102,27 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 + | pointer arithmetic failed: pointer must be in-bounds for 1 bytes at offset 0, but allocN has size 0 | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:15:50 + ::: $DIR/offset_ub.rs:17:50 | LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; - | --------------------------- inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:15:50 + | --------------------------- inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:17:50 -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) as *mut T } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | unable to turn bytes into a pointer + | 0x1 is not a valid pointer | inside `ptr::mut_ptr::::offset` at $SRC_DIR/core/src/ptr/mut_ptr.rs:LL:COL - | inside `DANGLING` at $DIR/offset_ub.rs:16:42 | - ::: $DIR/offset_ub.rs:16:1 + ::: $DIR/offset_ub.rs:18:42 | LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; - | --------------------------------------------------------------------------------------------- - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | ------------------------------------------------- inside `DANGLING` at $DIR/offset_ub.rs:18:42 error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -138,28 +133,24 @@ LL | unsafe { intrinsics::offset(self, count) } | pointer arithmetic failed: 0x0 is not a valid pointer | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/offset_ub.rs:19:50 + ::: $DIR/offset_ub.rs:21:50 | LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; - | --------------------------- inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:19:50 + | --------------------------- inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:21:50 -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | unable to turn bytes into a pointer + | 0x7f..f is not a valid pointer | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:22:47 | - ::: $DIR/offset_ub.rs:22:1 + ::: $DIR/offset_ub.rs:24:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; - | --------------------------------------------------------------------------------------------- - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | -------------------------------------------- inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:24:47 error: aborting due to 11 previous errors diff --git a/src/test/ui/consts/ptr_comparisons.rs b/src/test/ui/consts/ptr_comparisons.rs index aa8f511e07268..ffe8bc98446d0 100644 --- a/src/test/ui/consts/ptr_comparisons.rs +++ b/src/test/ui/consts/ptr_comparisons.rs @@ -1,6 +1,6 @@ // compile-flags: --crate-type=lib -// normalize-stderr-32bit: "offset 8" -> "offset $$TWO_WORDS" -// normalize-stderr-64bit: "offset 16" -> "offset $$TWO_WORDS" +// normalize-stderr-32bit: "8 bytes" -> "$$TWO_WORDS bytes" +// normalize-stderr-64bit: "16 bytes" -> "$$TWO_WORDS bytes" // normalize-stderr-32bit: "size 4" -> "size $$WORD" // normalize-stderr-64bit: "size 8" -> "size $$WORD" @@ -67,10 +67,10 @@ const _: *const u8 = const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 }; //~^ ERROR any use of this value will cause an error -//~| cannot cast pointer to integer +//~| unable to turn pointer into raw bytes //~| WARN this was previously accepted by the compiler but is being phased out const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 }; //~^ ERROR any use of this value will cause an error -//~| cannot cast pointer to integer +//~| unable to turn pointer into raw bytes //~| WARN this was previously accepted by the compiler but is being phased out diff --git a/src/test/ui/consts/ptr_comparisons.stderr b/src/test/ui/consts/ptr_comparisons.stderr index f00f40b325481..5c285e3fe7bc6 100644 --- a/src/test/ui/consts/ptr_comparisons.stderr +++ b/src/test/ui/consts/ptr_comparisons.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds at offset $TWO_WORDS, but is outside bounds of alloc2 which has size $WORD + | pointer arithmetic failed: pointer must be in-bounds for $TWO_WORDS bytes at offset 0, but alloc3 has size $WORD | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/ptr_comparisons.rs:61:34 @@ -16,7 +16,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ptr_comparisons.rs:64:33 | LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset 1000, but is outside bounds of alloc2 which has size $WORD + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 1000 bytes at offset 0, but alloc3 has size $WORD error: any use of this value will cause an error --> $DIR/ptr_comparisons.rs:68:27 @@ -24,7 +24,7 @@ error: any use of this value will cause an error LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 }; | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | cannot cast pointer to integer because it was not created by cast from integer + | unable to turn pointer into raw bytes | = note: `#[deny(const_err)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! @@ -36,7 +36,7 @@ error: any use of this value will cause an error LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 }; | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | cannot cast pointer to integer because it was not created by cast from integer + | unable to turn pointer into raw bytes | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #71800 diff --git a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr index 3a1b090012770..ff78c252731bd 100644 --- a/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr +++ b/src/test/ui/intrinsics/intrinsic-raw_eq-const-padding.stderr @@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/intrinsic-raw_eq-const-padding.rs:6:5 | LL | std::intrinsics::raw_eq(&(1_u8, 2_u16), &(1_u8, 2_u16)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc2, but 1 byte is uninitialized starting at alloc2+0x1, and this operation requires initialized memory + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading 4 bytes of memory starting at alloc3, but 1 byte is uninitialized starting at alloc3+0x1, and this operation requires initialized memory error: aborting due to previous error From 4ff353cd6e76e8b3b586b8c31b50669675a93e2c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 13 Jul 2021 00:25:31 +0200 Subject: [PATCH 06/16] test dynamic check for ptr-int-casts (unleashed) --- src/test/ui/consts/miri_unleashed/ptr_arith.rs | 9 ++++++++- .../ui/consts/miri_unleashed/ptr_arith.stderr | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.rs b/src/test/ui/consts/miri_unleashed/ptr_arith.rs index 902d67c126109..22314160c5e89 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.rs +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -11,7 +11,14 @@ static CMP: () = { //~| "pointer arithmetic or comparison" needs an rfc before being allowed inside constants }; -static INT_PTR_ARITH: () = unsafe { +static PTR_INT_CAST: () = { + let x = &0 as *const _ as usize; + //~^ ERROR could not evaluate static initializer + //~| unable to turn pointer into raw bytes + let _v = x == x; +}; + +static PTR_INT_TRANSMUTE: () = unsafe { let x: usize = std::mem::transmute(&0); let _v = x + 0; //~^ ERROR could not evaluate static initializer diff --git a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr index bd574a53a6082..2764d10348a76 100644 --- a/src/test/ui/consts/miri_unleashed/ptr_arith.stderr +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -5,7 +5,13 @@ LL | let _v = x == x; | ^^^^^^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants error[E0080]: could not evaluate static initializer - --> $DIR/ptr_arith.rs:16:14 + --> $DIR/ptr_arith.rs:15:13 + | +LL | let x = &0 as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes + +error[E0080]: could not evaluate static initializer + --> $DIR/ptr_arith.rs:23:14 | LL | let _v = x + 0; | ^^^^^ unable to turn pointer into raw bytes @@ -17,7 +23,12 @@ help: skipping check that does not even have a feature gate | LL | let _v = x == x; | ^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/ptr_arith.rs:15:13 + | +LL | let x = &0 as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0080`. From ae950a2dc78e51493034f543184b1664647dd8e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 14 Jul 2021 09:56:54 +0200 Subject: [PATCH 07/16] more precise message for the ptr access check on deref --- compiler/rustc_middle/src/mir/interpret/error.rs | 3 +++ compiler/rustc_middle/src/mir/interpret/mod.rs | 6 +++++- compiler/rustc_mir/src/interpret/place.rs | 15 +++++++-------- .../ui/consts/const-eval/ub-nonnull.32bit.stderr | 2 +- .../ui/consts/const-eval/ub-nonnull.64bit.stderr | 2 +- src/test/ui/consts/ptr_comparisons.stderr | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 29caf01af1f5b..02a32b9fc6ad3 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -170,6 +170,8 @@ impl fmt::Display for InvalidProgramInfo<'_> { /// Details of why a pointer had to be in-bounds. #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] pub enum CheckInAllocMsg { + /// We are dereferencing a pointer (i.e., creating a place). + DerefTest, /// We are access memory. MemoryAccessTest, /// We are doing pointer arithmetic. @@ -186,6 +188,7 @@ impl fmt::Display for CheckInAllocMsg { f, "{}", match *self { + CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ", CheckInAllocMsg::MemoryAccessTest => "memory access failed: ", CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ", CheckInAllocMsg::InboundsTest => "", diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 86bd35003bb1a..b05e3f5caa8aa 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -428,7 +428,11 @@ crate struct AllocMap<'tcx> { impl<'tcx> AllocMap<'tcx> { crate fn new() -> Self { - AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(NonZeroU64::new(1).unwrap()) } + AllocMap { + alloc_map: Default::default(), + dedup: Default::default(), + next_id: AllocId(NonZeroU64::new(1).unwrap()), + } } fn reserve(&mut self) -> AllocId { let next = self.next_id; diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 5e35a83a5b9b7..1c9905b775f0c 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -373,7 +373,7 @@ where let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); let mplace = self.ref_to_mplace(&val)?; - self.check_mplace_access(mplace)?; + self.check_mplace_access(mplace, CheckInAllocMsg::DerefTest)?; Ok(mplace) } @@ -400,18 +400,17 @@ where } /// Check if this mplace is dereferencable and sufficiently aligned. - pub fn check_mplace_access(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { + fn check_mplace_access( + &self, + mplace: MPlaceTy<'tcx, M::PointerTag>, + msg: CheckInAllocMsg, + ) -> InterpResult<'tcx> { let (size, align) = self .size_and_align_of_mplace(&mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); assert!(mplace.mplace.align <= align, "dynamic alignment less strict than static one?"); let align = M::enforce_alignment(&self.memory.extra).then_some(align); - self.memory.check_ptr_access_align( - mplace.ptr, - size, - align.unwrap_or(Align::ONE), - CheckInAllocMsg::MemoryAccessTest, // FIXME sth more specific? - )?; + self.memory.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?; Ok(()) } diff --git a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr index a0f69d5d4cc0c..bebdd22397557 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ memory access failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 + | ^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr index d1c2025821c54..0277506eff152 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ memory access failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 + | ^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/ptr_comparisons.stderr b/src/test/ui/consts/ptr_comparisons.stderr index 5c285e3fe7bc6..032b947b895a7 100644 --- a/src/test/ui/consts/ptr_comparisons.stderr +++ b/src/test/ui/consts/ptr_comparisons.stderr @@ -16,7 +16,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ptr_comparisons.rs:64:33 | LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 1000 bytes at offset 0, but alloc3 has size $WORD + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 1000 bytes at offset 0, but alloc3 has size $WORD error: any use of this value will cause an error --> $DIR/ptr_comparisons.rs:68:27 From 1f21ab114ee441d89a1a1f02f63bba638c0af01d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 14 Jul 2021 09:57:54 +0200 Subject: [PATCH 08/16] bless mir-opt, codegen, and remaining ui tests --- src/test/codegen/consts.rs | 2 +- src/test/codegen/remap_path_prefix/main.rs | 2 +- ...main.SimplifyCfg-elaborate-drops.after.mir | 6 +-- ..._allocation.main.ConstProp.after.32bit.mir | 40 ++++++++--------- ..._allocation.main.ConstProp.after.64bit.mir | 44 +++++++++---------- ...allocation2.main.ConstProp.after.32bit.mir | 38 ++++++++-------- ...allocation2.main.ConstProp.after.64bit.mir | 40 ++++++++--------- ...allocation3.main.ConstProp.after.32bit.mir | 24 +++++----- ...allocation3.main.ConstProp.after.64bit.mir | 22 +++++----- ...d[0].SimplifyCfg-elaborate-drops.after.mir | 8 ++-- ...motion_extern_static.BAR.PromoteTemps.diff | 8 ++-- ...d[0].SimplifyCfg-elaborate-drops.after.mir | 8 ++-- ...motion_extern_static.FOO.PromoteTemps.diff | 8 ++-- ...table_variable_no_prop.main.ConstProp.diff | 8 ++-- .../read_immutable_static.main.ConstProp.diff | 14 +++--- src/test/ui/error-codes/E0396-fixed.rs | 3 +- src/test/ui/error-codes/E0396-fixed.stderr | 11 ++--- 17 files changed, 140 insertions(+), 146 deletions(-) diff --git a/src/test/codegen/consts.rs b/src/test/codegen/consts.rs index 3aab4bea3d042..7f945299c22a6 100644 --- a/src/test/codegen/consts.rs +++ b/src/test/codegen/consts.rs @@ -10,7 +10,7 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc8 = {{.*}}, align 2 +// CHECK: @alloc9 = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used diff --git a/src/test/codegen/remap_path_prefix/main.rs b/src/test/codegen/remap_path_prefix/main.rs index c2d01c7fec231..698dfe6b4f341 100644 --- a/src/test/codegen/remap_path_prefix/main.rs +++ b/src/test/codegen/remap_path_prefix/main.rs @@ -12,7 +12,7 @@ mod aux_mod; include!("aux_mod.rs"); // Here we check that the expansion of the file!() macro is mapped. -// CHECK: @alloc1 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 +// CHECK: @alloc2 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }>, align 1 pub static FILE_PATH: &'static str = file!(); fn main() { diff --git a/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir index 3c56fce3f0a06..e22adae815c02 100644 --- a/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir +++ b/src/test/mir-opt/byte_slice.main.SimplifyCfg-elaborate-drops.after.mir @@ -16,10 +16,10 @@ fn main() -> () { _1 = const b"foo"; // scope 0 at $DIR/byte_slice.rs:5:13: 5:19 // ty::Const // + ty: &[u8; 3] - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/byte_slice.rs:5:13: 5:19 - // + literal: Const { ty: &[u8; 3], val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &[u8; 3], val: Value(Scalar(alloc1)) } StorageLive(_2); // scope 1 at $DIR/byte_slice.rs:6:9: 6:10 _2 = [const 5_u8, const 120_u8]; // scope 1 at $DIR/byte_slice.rs:6:13: 6:24 _0 = const (); // scope 0 at $DIR/byte_slice.rs:4:11: 7:2 @@ -29,6 +29,6 @@ fn main() -> () { } } -alloc0 (size: 3, align: 1) { +alloc1 (size: 3, align: 1) { 66 6f 6f │ foo } diff --git a/src/test/mir-opt/const_allocation.main.ConstProp.after.32bit.mir b/src/test/mir-opt/const_allocation.main.ConstProp.after.32bit.mir index a046539665b93..8e02dca4fb8b6 100644 --- a/src/test/mir-opt/const_allocation.main.ConstProp.after.32bit.mir +++ b/src/test/mir-opt/const_allocation.main.ConstProp.after.32bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - _2 = const {alloc0: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc1: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 // ty::Const // + ty: &&[(std::option::Option, &[&str])] - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation.rs:8:5: 8:8 - // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 @@ -23,43 +23,43 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 8, align: 4) { - ╾─alloc17─╼ 03 00 00 00 │ ╾──╼.... +alloc1 (static: FOO, size: 8, align: 4) { + ╾─alloc18─╼ 03 00 00 00 │ ╾──╼.... } -alloc17 (size: 48, align: 4) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼.... - 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼.... - 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼.... +alloc18 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc5──╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ....*...╾──╼.... } -alloc4 (size: 0, align: 4) {} +alloc5 (size: 0, align: 4) {} -alloc8 (size: 16, align: 4) { - ╾─alloc7──╼ 03 00 00 00 ╾─alloc9──╼ 03 00 00 00 │ ╾──╼....╾──╼.... +alloc9 (size: 16, align: 4) { + ╾─alloc8──╼ 03 00 00 00 ╾─alloc10─╼ 03 00 00 00 │ ╾──╼....╾──╼.... } -alloc7 (size: 3, align: 1) { +alloc8 (size: 3, align: 1) { 66 6f 6f │ foo } -alloc9 (size: 3, align: 1) { +alloc10 (size: 3, align: 1) { 62 61 72 │ bar } -alloc13 (size: 24, align: 4) { - 0x00 │ ╾─alloc12─╼ 03 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ╾──╼....╾──╼.... - 0x10 │ ╾─alloc15─╼ 04 00 00 00 │ ╾──╼.... +alloc14 (size: 24, align: 4) { + 0x00 │ ╾─alloc13─╼ 03 00 00 00 ╾─alloc15─╼ 03 00 00 00 │ ╾──╼....╾──╼.... + 0x10 │ ╾─alloc16─╼ 04 00 00 00 │ ╾──╼.... } -alloc12 (size: 3, align: 1) { +alloc13 (size: 3, align: 1) { 6d 65 68 │ meh } -alloc14 (size: 3, align: 1) { +alloc15 (size: 3, align: 1) { 6d 6f 70 │ mop } -alloc15 (size: 4, align: 1) { +alloc16 (size: 4, align: 1) { 6d c3 b6 70 │ m..p } diff --git a/src/test/mir-opt/const_allocation.main.ConstProp.after.64bit.mir b/src/test/mir-opt/const_allocation.main.ConstProp.after.64bit.mir index 23859439208fe..88cdbda2fae36 100644 --- a/src/test/mir-opt/const_allocation.main.ConstProp.after.64bit.mir +++ b/src/test/mir-opt/const_allocation.main.ConstProp.after.64bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 - _2 = const {alloc0: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 + _2 = const {alloc1: &&[(Option, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 // ty::Const // + ty: &&[(std::option::Option, &[&str])] - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation.rs:8:5: 8:8 - // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&[(std::option::Option, &[&str])], val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8 StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9 @@ -23,47 +23,47 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 16, align: 8) { - ╾───────alloc17───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc1 (static: FOO, size: 16, align: 8) { + ╾───────alloc18───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc17 (size: 72, align: 8) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼ +alloc18 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc5────────╼ │ ....░░░░╾──────╼ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ - 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼ + 0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc14───────╼ │ ....*...╾──────╼ 0x40 │ 03 00 00 00 00 00 00 00 │ ........ } -alloc4 (size: 0, align: 8) {} +alloc5 (size: 0, align: 8) {} -alloc8 (size: 32, align: 8) { - 0x00 │ ╾───────alloc7────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc9 (size: 32, align: 8) { + 0x00 │ ╾───────alloc8────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc10───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc7 (size: 3, align: 1) { +alloc8 (size: 3, align: 1) { 66 6f 6f │ foo } -alloc9 (size: 3, align: 1) { +alloc10 (size: 3, align: 1) { 62 61 72 │ bar } -alloc13 (size: 48, align: 8) { - 0x00 │ ╾───────alloc12───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x10 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x20 │ ╾───────alloc15───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc14 (size: 48, align: 8) { + 0x00 │ ╾───────alloc13───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x10 │ ╾───────alloc15───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x20 │ ╾───────alloc16───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc12 (size: 3, align: 1) { +alloc13 (size: 3, align: 1) { 6d 65 68 │ meh } -alloc14 (size: 3, align: 1) { +alloc15 (size: 3, align: 1) { 6d 6f 70 │ mop } -alloc15 (size: 4, align: 1) { +alloc16 (size: 4, align: 1) { 6d c3 b6 70 │ m..p } diff --git a/src/test/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir b/src/test/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir index 1e4bd4ba914d5..059b721f5bb63 100644 --- a/src/test/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir +++ b/src/test/mir-opt/const_allocation2.main.ConstProp.after.32bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - _2 = const {alloc0: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc1: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 // ty::Const // + ty: &&[(std::option::Option, &[&u8])] - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation2.rs:5:5: 5:8 - // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 @@ -23,42 +23,42 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 8, align: 4) { - ╾─alloc27─╼ 03 00 00 00 │ ╾──╼.... +alloc1 (static: FOO, size: 8, align: 4) { + ╾─alloc28─╼ 03 00 00 00 │ ╾──╼.... } -alloc27 (size: 48, align: 4) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc12─╼ 00 00 00 00 │ ....░░░░╾──╼.... - 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc17─╼ 02 00 00 00 │ ....░░░░╾──╼.... - 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc25─╼ 03 00 00 00 │ ....*...╾──╼.... +alloc28 (size: 48, align: 4) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc13─╼ 00 00 00 00 │ ....░░░░╾──╼.... + 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc18─╼ 02 00 00 00 │ ....░░░░╾──╼.... + 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc26─╼ 03 00 00 00 │ ....*...╾──╼.... } -alloc12 (size: 0, align: 4) {} +alloc13 (size: 0, align: 4) {} -alloc17 (size: 8, align: 4) { - ╾─alloc15─╼ ╾─alloc16─╼ │ ╾──╼╾──╼ +alloc18 (size: 8, align: 4) { + ╾─alloc16─╼ ╾─alloc17─╼ │ ╾──╼╾──╼ } -alloc15 (size: 1, align: 1) { +alloc16 (size: 1, align: 1) { 05 │ . } -alloc16 (size: 1, align: 1) { +alloc17 (size: 1, align: 1) { 06 │ . } -alloc25 (size: 12, align: 4) { - ╾─a21+0x3─╼ ╾─alloc22─╼ ╾─a24+0x2─╼ │ ╾──╼╾──╼╾──╼ +alloc26 (size: 12, align: 4) { + ╾─a22+0x3─╼ ╾─alloc23─╼ ╾─a25+0x2─╼ │ ╾──╼╾──╼╾──╼ } -alloc21 (size: 4, align: 1) { +alloc22 (size: 4, align: 1) { 2a 45 15 6f │ *E.o } -alloc22 (size: 1, align: 1) { +alloc23 (size: 1, align: 1) { 2a │ * } -alloc24 (size: 4, align: 1) { +alloc25 (size: 4, align: 1) { 2a 45 15 6f │ *E.o } diff --git a/src/test/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir b/src/test/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir index f943beced3109..d6a97917d6226 100644 --- a/src/test/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir +++ b/src/test/mir-opt/const_allocation2.main.ConstProp.after.64bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 - _2 = const {alloc0: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 + _2 = const {alloc1: &&[(Option, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 // ty::Const // + ty: &&[(std::option::Option, &[&u8])] - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation2.rs:5:5: 5:8 - // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&[(std::option::Option, &[&u8])], val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8 StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9 @@ -23,45 +23,45 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 16, align: 8) { - ╾───────alloc27───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ +alloc1 (static: FOO, size: 16, align: 8) { + ╾───────alloc28───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........ } -alloc27 (size: 72, align: 8) { - 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc12───────╼ │ ....░░░░╾──────╼ +alloc28 (size: 72, align: 8) { + 0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc13───────╼ │ ....░░░░╾──────╼ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░ - 0x20 │ ╾───────alloc17───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ - 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc25───────╼ │ ....*...╾──────╼ + 0x20 │ ╾───────alloc18───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........ + 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc26───────╼ │ ....*...╾──────╼ 0x40 │ 03 00 00 00 00 00 00 00 │ ........ } -alloc12 (size: 0, align: 8) {} +alloc13 (size: 0, align: 8) {} -alloc17 (size: 16, align: 8) { - ╾───────alloc15───────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼ +alloc18 (size: 16, align: 8) { + ╾───────alloc16───────╼ ╾───────alloc17───────╼ │ ╾──────╼╾──────╼ } -alloc15 (size: 1, align: 1) { +alloc16 (size: 1, align: 1) { 05 │ . } -alloc16 (size: 1, align: 1) { +alloc17 (size: 1, align: 1) { 06 │ . } -alloc25 (size: 24, align: 8) { - 0x00 │ ╾─────alloc21+0x3─────╼ ╾───────alloc22───────╼ │ ╾──────╼╾──────╼ - 0x10 │ ╾─────alloc24+0x2─────╼ │ ╾──────╼ +alloc26 (size: 24, align: 8) { + 0x00 │ ╾─────alloc22+0x3─────╼ ╾───────alloc23───────╼ │ ╾──────╼╾──────╼ + 0x10 │ ╾─────alloc25+0x2─────╼ │ ╾──────╼ } -alloc21 (size: 4, align: 1) { +alloc22 (size: 4, align: 1) { 2a 45 15 6f │ *E.o } -alloc22 (size: 1, align: 1) { +alloc23 (size: 1, align: 1) { 2a │ * } -alloc24 (size: 4, align: 1) { +alloc25 (size: 4, align: 1) { 2a 45 15 6f │ *E.o } diff --git a/src/test/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir b/src/test/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir index 5c95afb586ff2..32000340dcebf 100644 --- a/src/test/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir +++ b/src/test/mir-opt/const_allocation3.main.ConstProp.after.32bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc1: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 // ty::Const // + ty: &&Packed - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation3.rs:5:5: 5:8 - // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 @@ -23,31 +23,31 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 4, align: 4) { - ╾─alloc10─╼ │ ╾──╼ +alloc1 (static: FOO, size: 4, align: 4) { + ╾─alloc11─╼ │ ╾──╼ } -alloc10 (size: 168, align: 1) { +alloc11 (size: 168, align: 1) { 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ - 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc5──╼ │ ............╾──╼ + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc6──╼ │ ............╾──╼ 0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ - 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc7──╼ 00 00 │ ..........╾──╼.. - 0x90 │ ╾─a8+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............ + 0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾─alloc8──╼ 00 00 │ ..........╾──╼.. + 0x90 │ ╾─a9+0x63─╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............ 0xa0 │ 00 00 00 00 00 00 00 00 │ ........ } -alloc5 (size: 4, align: 4) { +alloc6 (size: 4, align: 4) { 2a 00 00 00 │ *... } -alloc7 (fn: main) +alloc8 (fn: main) -alloc8 (size: 100, align: 1) { +alloc9 (size: 100, align: 1) { 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ diff --git a/src/test/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir b/src/test/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir index 3d1858de22e75..cac882333aeb9 100644 --- a/src/test/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir +++ b/src/test/mir-opt/const_allocation3.main.ConstProp.after.64bit.mir @@ -8,13 +8,13 @@ fn main() -> () { bb0: { StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 - _2 = const {alloc0: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 + _2 = const {alloc1: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 // ty::Const // + ty: &&Packed - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const_allocation3.rs:5:5: 5:8 - // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc1)) } _1 = (*_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8 StorageDead(_2); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 StorageDead(_1); // scope 0 at $DIR/const_allocation3.rs:5:8: 5:9 @@ -23,13 +23,13 @@ fn main() -> () { } } -alloc0 (static: FOO, size: 8, align: 8) { - ╾───────alloc10───────╼ │ ╾──────╼ +alloc1 (static: FOO, size: 8, align: 8) { + ╾───────alloc11───────╼ │ ╾──────╼ } -alloc10 (size: 180, align: 1) { +alloc11 (size: 180, align: 1) { 0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................ - 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc5── │ ............╾─── + 0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾──alloc6── │ ............╾─── 0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............ 0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ @@ -37,18 +37,18 @@ alloc10 (size: 180, align: 1) { 0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─ - 0x90 │ ─────alloc7─────╼ 00 00 ╾─────alloc8+0x63─────╼ │ ─────╼..╾──────╼ + 0x90 │ ─────alloc8─────╼ 00 00 ╾─────alloc9+0x63─────╼ │ ─────╼..╾──────╼ 0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0xb0 │ 00 00 00 00 │ .... } -alloc5 (size: 4, align: 4) { +alloc6 (size: 4, align: 4) { 2a 00 00 00 │ *... } -alloc7 (fn: main) +alloc8 (fn: main) -alloc8 (size: 100, align: 1) { +alloc9 (size: 100, align: 1) { 0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ 0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................ diff --git a/src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir index 2497c553e6970..a64c960b9f716 100644 --- a/src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir +++ b/src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir @@ -7,13 +7,13 @@ promoted[0] in BAR: &[&i32; 1] = { let mut _3: &i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 bb0: { - _3 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 + _3 = const {alloc1: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 // ty::Const // + ty: &i32 - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 - // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) } _2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 @@ -21,6 +21,6 @@ promoted[0] in BAR: &[&i32; 1] = { } } -alloc0 (static: Y, size: 4, align: 4) { +alloc1 (static: Y, size: 4, align: 4) { 2a 00 00 00 │ *... } diff --git a/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff index 8bb5dbc48feef..6f4bd65b85d4d 100644 --- a/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff +++ b/src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff @@ -16,16 +16,16 @@ - StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 - StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 -- _5 = const {alloc0: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 +- _5 = const {alloc1: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34 + _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 // ty::Const - // + ty: &i32 -- // + val: Value(Scalar(alloc0)) +- // + val: Value(Scalar(alloc1)) + // + ty: &[&i32; 1] + // + val: Unevaluated(BAR, [], Some(promoted[0])) // mir::Constant - // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34 -- // + literal: Const { ty: &i32, val: Value(Scalar(alloc0)) } +- // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) } - _4 = &(*_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34 - _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 - _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35 @@ -53,7 +53,7 @@ } - } - -- alloc0 (static: Y, size: 4, align: 4) { +- alloc1 (static: Y, size: 4, align: 4) { - 2a 00 00 00 │ *... } diff --git a/src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir index 17107ec431eff..8b3c5d332f251 100644 --- a/src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir +++ b/src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir @@ -7,13 +7,13 @@ promoted[0] in FOO: &[&i32; 1] = { let mut _3: *const i32; // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 bb0: { - _3 = const {alloc2: *const i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 + _3 = const {alloc3: *const i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 // ty::Const // + ty: *const i32 - // + val: Value(Scalar(alloc2)) + // + val: Value(Scalar(alloc3)) // mir::Constant // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 - // + literal: Const { ty: *const i32, val: Value(Scalar(alloc2)) } + // + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) } _2 = &(*_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 _1 = [move _2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 _0 = &_1; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 @@ -21,4 +21,4 @@ promoted[0] in FOO: &[&i32; 1] = { } } -alloc2 (extern static: X) +alloc3 (extern static: X) diff --git a/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff index 54f1200a21fe1..8c9a37bce854b 100644 --- a/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff +++ b/src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff @@ -18,16 +18,16 @@ - StorageLive(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - StorageLive(_4); // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45 - StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 -- _5 = const {alloc2: *const i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 +- _5 = const {alloc3: *const i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43 + _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 // ty::Const - // + ty: *const i32 -- // + val: Value(Scalar(alloc2)) +- // + val: Value(Scalar(alloc3)) + // + ty: &[&i32; 1] + // + val: Unevaluated(FOO, [], Some(promoted[0])) // mir::Constant - // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43 -- // + literal: Const { ty: *const i32, val: Value(Scalar(alloc2)) } +- // + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) } - _4 = &(*_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43 - _3 = [move _4]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 - _2 = &_3; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46 @@ -55,5 +55,5 @@ } } - -- alloc2 (extern static: X) +- alloc3 (extern static: X) diff --git a/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff index 5dad8e721fb13..afc621784633e 100644 --- a/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/mutable_variable_no_prop.main.ConstProp.diff @@ -23,13 +23,13 @@ StorageLive(_2); // scope 1 at $DIR/mutable_variable_no_prop.rs:8:5: 10:6 StorageLive(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 StorageLive(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - _4 = const {alloc0: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 + _4 = const {alloc1: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 // ty::Const // + ty: *mut u32 - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/mutable_variable_no_prop.rs:9:13: 9:19 - // + literal: Const { ty: *mut u32, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: *mut u32, val: Value(Scalar(alloc1)) } _3 = (*_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19 _1 = move _3; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:9: 9:19 StorageDead(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:18: 9:19 @@ -45,7 +45,7 @@ } } - alloc0 (static: STATIC, size: 4, align: 4) { + alloc1 (static: STATIC, size: 4, align: 4) { 2a 00 00 00 │ *... } diff --git a/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff b/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff index 0d141a070ff62..13ef30d89a076 100644 --- a/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/read_immutable_static.main.ConstProp.diff @@ -16,24 +16,24 @@ StorageLive(_1); // scope 0 at $DIR/read_immutable_static.rs:7:9: 7:10 StorageLive(_2); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 StorageLive(_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 - _3 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + _3 = const {alloc1: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 // ty::Const // + ty: &u8 - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/read_immutable_static.rs:7:13: 7:16 - // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &u8, val: Value(Scalar(alloc1)) } - _2 = (*_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 + _2 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16 StorageLive(_4); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 StorageLive(_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - _5 = const {alloc0: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 + _5 = const {alloc1: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 // ty::Const // + ty: &u8 - // + val: Value(Scalar(alloc0)) + // + val: Value(Scalar(alloc1)) // mir::Constant // + span: $DIR/read_immutable_static.rs:7:19: 7:22 - // + literal: Const { ty: &u8, val: Value(Scalar(alloc0)) } + // + literal: Const { ty: &u8, val: Value(Scalar(alloc1)) } - _4 = (*_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 - _1 = Add(move _2, move _4); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:22 + _4 = const 2_u8; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22 @@ -48,7 +48,7 @@ } } - alloc0 (static: FOO, size: 1, align: 1) { + alloc1 (static: FOO, size: 1, align: 1) { 02 │ . } diff --git a/src/test/ui/error-codes/E0396-fixed.rs b/src/test/ui/error-codes/E0396-fixed.rs index 76dd857ea5616..654b21f05b6fe 100644 --- a/src/test/ui/error-codes/E0396-fixed.rs +++ b/src/test/ui/error-codes/E0396-fixed.rs @@ -3,8 +3,7 @@ const REG_ADDR: *const u8 = 0x5f3759df as *const u8; const VALUE: u8 = unsafe { *REG_ADDR }; -//~^ ERROR any use of this value will cause an error -//~| WARN this was previously accepted by the compiler but is being phased out +//~^ ERROR evaluation of constant value failed fn main() { } diff --git a/src/test/ui/error-codes/E0396-fixed.stderr b/src/test/ui/error-codes/E0396-fixed.stderr index 521394bdc8e01..91997fcf0f901 100644 --- a/src/test/ui/error-codes/E0396-fixed.stderr +++ b/src/test/ui/error-codes/E0396-fixed.stderr @@ -1,14 +1,9 @@ -error: any use of this value will cause an error +error[E0080]: evaluation of constant value failed --> $DIR/E0396-fixed.rs:5:28 | LL | const VALUE: u8 = unsafe { *REG_ADDR }; - | ---------------------------^^^^^^^^^--- - | | - | unable to turn bytes into a pointer - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 + | ^^^^^^^^^ 0x5f3759df is not a valid pointer error: aborting due to previous error +For more information about this error, try `rustc --explain E0080`. From 8932aebfdfc8e4be18ed5213ba24f72954c7ba47 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 14 Jul 2021 10:52:08 +0200 Subject: [PATCH 09/16] remove unused error variant --- compiler/rustc_middle/src/mir/interpret/error.rs | 3 --- compiler/rustc_mir/src/interpret/validity.rs | 11 +++-------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 02a32b9fc6ad3..8424123827662 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -397,8 +397,6 @@ pub enum UnsupportedOpInfo { // // The variants below are only reachable from CTFE/const prop, miri will never emit them. // - /// Encountered raw bytes where we needed a pointer. - ReadBytesAsPointer, /// Accessing thread local statics ThreadLocalStatic(DefId), /// Accessing an unsupported extern static. @@ -413,7 +411,6 @@ impl fmt::Display for UnsupportedOpInfo { ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did), NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did), ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), - ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), } } diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index c633c87e19e41..2d4eb0b823854 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -335,8 +335,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ), self.path, err_ub!(DanglingIntPointer(..)) | - err_ub!(PointerUseAfterFree(..)) | - err_unsup!(ReadBytesAsPointer) => + err_ub!(PointerUseAfterFree(..)) => { "dangling vtable pointer in wide pointer" }, err_ub!(AlignmentCheckFailed { .. }) => { "unaligned vtable pointer in wide pointer" }, @@ -347,8 +346,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' self.ecx.read_drop_type_from_vtable(vtable), self.path, err_ub!(DanglingIntPointer(..)) | - err_ub!(InvalidFunctionPointer(..)) | - err_unsup!(ReadBytesAsPointer) => + 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)" }, @@ -437,8 +435,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "a dangling {} (address 0x{:x} is unallocated)", kind, i }, err_ub!(PointerOutOfBounds { .. }) => { "a dangling {} (going beyond the bounds of its allocation)", kind }, - err_unsup!(ReadBytesAsPointer) => - { "a dangling {} (created from integer)", kind }, // This cannot happen during const-eval (because interning already detects // dangling pointers), but it can happen in Miri. err_ub!(PointerUseAfterFree(..)) => @@ -598,8 +594,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' self.path, err_ub!(DanglingIntPointer(..)) | err_ub!(InvalidFunctionPointer(..)) | - err_ub!(InvalidUninitBytes(None)) | - err_unsup!(ReadBytesAsPointer) => + err_ub!(InvalidUninitBytes(None)) => { "{}", value } expected { "a function pointer" }, ); // FIXME: Check if the signature matches From f4b61ba509e71710df5c14ac282fbdd512344072 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 14 Jul 2021 22:10:17 +0200 Subject: [PATCH 10/16] adjustions and cleanup to make Miri build again --- .../rustc_middle/src/mir/interpret/pointer.rs | 14 ++- .../rustc_middle/src/mir/interpret/value.rs | 54 +-------- compiler/rustc_middle/src/ty/consts/int.rs | 11 +- compiler/rustc_mir/src/const_eval/machine.rs | 2 +- compiler/rustc_mir/src/const_eval/mod.rs | 2 +- compiler/rustc_mir/src/interpret/cast.rs | 4 +- compiler/rustc_mir/src/interpret/intern.rs | 6 +- .../rustc_mir/src/interpret/intrinsics.rs | 12 +- compiler/rustc_mir/src/interpret/machine.rs | 108 ++++++++---------- compiler/rustc_mir/src/interpret/memory.rs | 73 ++++++------ compiler/rustc_mir/src/interpret/place.rs | 42 ++++--- compiler/rustc_mir/src/interpret/step.rs | 5 +- .../rustc_mir/src/transform/const_prop.rs | 2 +- 13 files changed, 134 insertions(+), 201 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index a95e39e181130..307d7d2846208 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -171,7 +171,7 @@ impl From> for Pointer> { } impl Pointer> { - pub fn into_pointer_or_offset(self) -> Result, Size> { + pub fn into_pointer_or_addr(self) -> Result, Size> { match self.provenance { Some(tag) => Ok(Pointer::new(tag, self.offset)), None => Err(self.offset), @@ -187,6 +187,13 @@ impl Pointer> { } } +impl Pointer> { + #[inline(always)] + pub fn null() -> Self { + Pointer { provenance: None, offset: Size::ZERO } + } +} + impl<'tcx, Tag> Pointer { #[inline(always)] pub fn new(provenance: Tag, offset: Size) -> Self { @@ -206,9 +213,14 @@ impl<'tcx, Tag> Pointer { where Tag: Provenance, { + // FIXME: This is wrong! `self.offset` might be an absolute address. Pointer { offset: self.offset, provenance: self.provenance.erase_for_fmt() } } + pub fn map_provenance(self, f: impl FnOnce(Tag) -> Tag) -> Self { + Pointer { provenance: f(self.provenance), ..self } + } + #[inline] pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { Ok(Pointer { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index bb6f1bb21c623..1be04f8c18b1b 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -6,7 +6,7 @@ use rustc_apfloat::{ Float, }; use rustc_macros::HashStable; -use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; +use rustc_target::abi::{HasDataLayout, Size}; use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt}; @@ -179,7 +179,7 @@ impl From for Scalar { } } -impl<'tcx, Tag> Scalar { +impl Scalar { pub const ZST: Self = Scalar::Int(ScalarInt::ZST); #[inline(always)] @@ -202,56 +202,6 @@ impl<'tcx, Tag> Scalar { Scalar::Int(ScalarInt::null(cx.pointer_size())) } - #[inline(always)] - fn ptr_op( - self, - dl: &TargetDataLayout, - f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>, - f_ptr: impl FnOnce(Pointer) -> InterpResult<'tcx, Pointer>, - ) -> InterpResult<'tcx, Self> { - match self { - Scalar::Int(int) => Ok(Scalar::Int(int.ptr_sized_op(dl, f_int)?)), - Scalar::Ptr(ptr, sz) => { - debug_assert_eq!(u64::from(sz), dl.pointer_size().bytes()); - Ok(Scalar::Ptr(f_ptr(ptr)?, sz)) - } - } - } - - #[inline] - pub fn ptr_offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - let dl = cx.data_layout(); - self.ptr_op(dl, |int| dl.offset(int, i.bytes()), |ptr| ptr.offset(i, dl)) - } - - #[inline] - pub fn ptr_wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self { - let dl = cx.data_layout(); - self.ptr_op( - dl, - |int| Ok(dl.overflowing_offset(int, i.bytes()).0), - |ptr| Ok(ptr.wrapping_offset(i, dl)), - ) - .unwrap() - } - - #[inline] - pub fn ptr_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> { - let dl = cx.data_layout(); - self.ptr_op(dl, |int| dl.signed_offset(int, i), |ptr| ptr.signed_offset(i, dl)) - } - - #[inline] - pub fn ptr_wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self { - let dl = cx.data_layout(); - self.ptr_op( - dl, - |int| Ok(dl.overflowing_signed_offset(int, i).0), - |ptr| Ok(ptr.wrapping_signed_offset(i, dl)), - ) - .unwrap() - } - #[inline] pub fn from_bool(b: bool) -> Self { Scalar::Int(b.into()) diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 8ed8ea6a0bc55..8262bc261996a 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -1,7 +1,7 @@ use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_target::abi::{Size, TargetDataLayout}; +use rustc_target::abi::Size; use std::convert::{TryFrom, TryInto}; use std::fmt; @@ -193,15 +193,6 @@ impl ScalarInt { self.data == 0 } - pub(crate) fn ptr_sized_op( - self, - dl: &TargetDataLayout, - f_int: impl FnOnce(u64) -> Result, - ) -> Result { - assert_eq!(u64::from(self.size), dl.pointer_size.bytes()); - Ok(Self::try_from_uint(f_int(u64::try_from(self.data).unwrap())?, self.size()).unwrap()) - } - #[inline] pub fn try_from_uint(i: impl Into, size: Size) -> Option { let data = i.into(); diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 2bebbc65c24c2..c809f4f273aa5 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -312,7 +312,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, align, interpret::MemoryKind::Machine(MemoryKind::Heap), )?; - ecx.write_scalar(Scalar::from_pointer(ptr, &*ecx.tcx), dest)?; + ecx.write_pointer(ptr, dest)?; } _ => { return Err(ConstEvalErrKind::NeedsRfc(format!( diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index 78124428787cd..a334165df4cb1 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -35,7 +35,7 @@ pub(crate) fn const_caller_location( if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { bug!("intern_const_alloc_recursive should not error in this case") } - ConstValue::Scalar(Scalar::from_pointer(loc_place.ptr.into_pointer_or_offset().unwrap(), &tcx)) + ConstValue::Scalar(Scalar::from_pointer(loc_place.ptr.into_pointer_or_addr().unwrap(), &tcx)) } /// Convert an evaluated constant to a type level constant diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index ca7fd7010f3d1..514c1aa9646a0 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -57,7 +57,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .ok_or_else(|| err_inval!(TooGeneric))?; let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(Scalar::from_pointer(fn_ptr, &*self.tcx), dest)?; + self.write_pointer(fn_ptr, dest)?; } _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), } @@ -88,7 +88,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ClosureKind::FnOnce, ); let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - self.write_scalar(Scalar::from_pointer(fn_ptr, &*self.tcx), dest)?; + self.write_pointer(fn_ptr, dest)?; } _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), } diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index b0606aba4164b..f2457d11d9ee3 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -23,7 +23,7 @@ use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; use rustc_ast::Mutability; -use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, ValueVisitor}; +use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, ValueVisitor}; use crate::const_eval; pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine< @@ -425,11 +425,11 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> layout: TyAndLayout<'tcx>, f: impl FnOnce( &mut InterpCx<'mir, 'tcx, M>, - &MPlaceTy<'tcx, M::PointerTag>, + &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, &'tcx Allocation> { let dest = self.allocate(layout, MemoryKind::Stack)?; - f(self, &dest)?; + f(self, &dest.into())?; let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1; alloc.mutability = Mutability::Not; Ok(self.tcx.intern_const_alloc(alloc)) diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 4e2db2f13f177..9335b783c7751 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -337,17 +337,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let pointee_ty = substs.type_at(0); let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; - self.write_scalar(Scalar::from_maybe_pointer(offset_ptr, self), dest)?; + self.write_pointer(offset_ptr, dest)?; } sym::arith_offset => { - let ptr = self.read_scalar(&args[0])?.check_init()?; + let ptr = self.read_pointer(&args[0])?; let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?; let pointee_ty = substs.type_at(0); let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); let offset_bytes = offset_count.wrapping_mul(pointee_size); - let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); - self.write_scalar(offset_ptr, dest)?; + let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, self); + self.write_pointer(offset_ptr, dest)?; } sym::ptr_offset_from => { let a = self.read_immediate(&args[0])?.to_scalar()?; @@ -379,8 +379,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // General case: we need two pointers. let a = self.scalar_to_ptr(a); let b = self.scalar_to_ptr(b); - let (a_alloc_id, a_offset, _) = self.memory.ptr_force_alloc(a)?; - let (b_alloc_id, b_offset, _) = self.memory.ptr_force_alloc(b)?; + let (a_alloc_id, a_offset, _) = self.memory.ptr_get_alloc(a)?; + let (b_alloc_id, b_offset, _) = self.memory.ptr_get_alloc(b)?; if a_alloc_id != b_alloc_id { throw_ub_format!( "ptr_offset_from cannot compute offset of pointers into different \ diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index c135e4f996342..7b8f2aecd0d47 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -7,14 +7,14 @@ use std::fmt::Debug; use std::hash::Hash; use rustc_middle::mir; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use super::{ - AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, LocalValue, MemPlace, Memory, - MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, + AllocId, AllocRange, Allocation, Frame, ImmTy, InterpCx, InterpResult, LocalValue, MemPlace, + Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, }; /// Data returned by Machine::stack_pop, @@ -262,34 +262,40 @@ pub trait Machine<'mir, 'tcx>: Sized { } /// Return the `AllocId` for the given thread-local static in the current thread. - fn thread_local_static_alloc_id( + fn thread_local_static_base_pointer( _ecx: &mut InterpCx<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, AllocId> { + ) -> InterpResult<'tcx, Pointer> { throw_unsup!(ThreadLocalStatic(def_id)) } - /// Return the `AllocId` backing the given `extern static`. - fn extern_static_alloc_id( + /// Return the root pointer for the given `extern static`. + fn extern_static_base_pointer( mem: &Memory<'mir, 'tcx, Self>, def_id: DefId, - ) -> InterpResult<'tcx, AllocId> { - // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. - Ok(mem.tcx.create_static_alloc(def_id)) - } + ) -> InterpResult<'tcx, Pointer>; - /// Return the "base" tag for the given *global* allocation: the one that is used for direct - /// accesses to this static/const/fn allocation. If `id` is not a global allocation, - /// this will return an unusable tag (i.e., accesses will be UB)! - /// - /// Called on the id returned by `thread_local_static_alloc_id` and `extern_static_alloc_id`, if needed. + /// Return a "base" pointer for the given allocation: the one that is used for direct + /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. /// - /// `offset` is relative inside the allocation. - fn tag_global_base_pointer( - memory_extra: &Self::MemoryExtra, + /// Not called on `extern` or thread-local statics (those use the methods above). + fn tag_alloc_base_pointer( + mem: &Memory<'mir, 'tcx, Self>, ptr: Pointer, ) -> Pointer; + /// "Int-to-pointer cast" + fn ptr_from_addr( + mem: &Memory<'mir, 'tcx, Self>, + addr: u64, + ) -> Pointer>; + + /// Convert a pointer with provenance into an allocation-offset pair. + fn ptr_get_alloc( + mem: &Memory<'mir, 'tcx, Self>, + ptr: Pointer, + ) -> (AllocId, Size); + /// Called to initialize the "extra" state of an allocation and make the pointers /// it contains (in relocations) tagged. The way we construct allocations is /// to always first construct it without extra and then add the extra. @@ -303,16 +309,13 @@ pub trait Machine<'mir, 'tcx>: Sized { /// allocation (because a copy had to be done to add tags or metadata), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) - /// - /// Also return the "base" tag to use for this allocation: the one that is used for direct - /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent - /// with `tag_global_base_pointer`. fn init_allocation_extra<'b>( memory_extra: &Self::MemoryExtra, + tcx: TyCtxt<'tcx>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag); + ) -> Cow<'b, Allocation>; /// Hook for performing extra checks on a memory read access. /// @@ -323,8 +326,8 @@ pub trait Machine<'mir, 'tcx>: Sized { fn memory_read( _memory_extra: &Self::MemoryExtra, _alloc_extra: &Self::AllocExtra, - _ptr: Pointer, - _size: Size, + _tag: Self::PointerTag, + _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) } @@ -334,8 +337,8 @@ pub trait Machine<'mir, 'tcx>: Sized { fn memory_written( _memory_extra: &mut Self::MemoryExtra, _alloc_extra: &mut Self::AllocExtra, - _ptr: Pointer, - _size: Size, + _tag: Self::PointerTag, + _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) } @@ -345,17 +348,8 @@ pub trait Machine<'mir, 'tcx>: Sized { fn memory_deallocated( _memory_extra: &mut Self::MemoryExtra, _alloc_extra: &mut Self::AllocExtra, - _ptr: Pointer, - _size: Size, - ) -> InterpResult<'tcx> { - Ok(()) - } - - /// Called after initializing static memory using the interpreter. - fn after_static_mem_initialized( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _ptr: Pointer, - _size: Size, + _tag: Self::PointerTag, + _range: AllocRange, ) -> InterpResult<'tcx> { Ok(()) } @@ -400,19 +394,6 @@ pub trait Machine<'mir, 'tcx>: Sized { // By default, we do not support unwinding from panics Ok(StackPopJump::Normal) } - - /// "Int-to-pointer cast" - fn ptr_from_addr( - mem: &Memory<'mir, 'tcx, Self>, - addr: u64, - ) -> Pointer>; - - /// Convert a pointer with provenance into an allocation-offset pair, - /// or a `None` with an absolute address if that conversion is not possible. - fn ptr_get_alloc( - mem: &Memory<'mir, 'tcx, Self>, - ptr: Pointer, - ) -> (Option, Size); } // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines @@ -461,17 +442,26 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn init_allocation_extra<'b>( _memory_extra: &Self::MemoryExtra, - id: AllocId, + _tcx: TyCtxt<$tcx>, + _id: AllocId, alloc: Cow<'b, Allocation>, _kind: Option>, - ) -> (Cow<'b, Allocation>, Self::PointerTag) { + ) -> Cow<'b, Allocation> { // We do not use a tag so we can just cheaply forward the allocation - (alloc, id) + alloc + } + + fn extern_static_base_pointer( + mem: &Memory<$mir, $tcx, Self>, + def_id: DefId, + ) -> InterpResult<$tcx, Pointer> { + // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail. + Ok(Pointer::new(mem.tcx.create_static_alloc(def_id), Size::ZERO)) } #[inline(always)] - fn tag_global_base_pointer( - _memory_extra: &Self::MemoryExtra, + fn tag_alloc_base_pointer( + _mem: &Memory<$mir, $tcx, Self>, ptr: Pointer, ) -> Pointer { ptr @@ -486,9 +476,9 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn ptr_get_alloc( _mem: &Memory<$mir, $tcx, Self>, ptr: Pointer, - ) -> (Option, Size) { + ) -> (AllocId, Size) { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); - (Some(alloc_id), offset) + (alloc_id, offset) } } diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 5e5a969c26ab8..aae3721e4e4fb 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -168,20 +168,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); // We need to handle `extern static`. - let alloc_id = match self.tcx.get_global_alloc(alloc_id) { + match self.tcx.get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => { bug!("global memory cannot point to thread-local static") } Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => { - M::extern_static_alloc_id(self, def_id)? + return M::extern_static_base_pointer(self, def_id); } - _ => { - // No need to change the `AllocId`. - alloc_id - } - }; + _ => {} + } // And we need to get the tag. - Ok(M::tag_global_base_pointer(&self.extra, Pointer::new(alloc_id, offset))) + Ok(M::tag_alloc_base_pointer(self, Pointer::new(alloc_id, offset))) } pub fn create_fn_alloc( @@ -236,9 +233,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { "dynamically allocating global memory" ); // This is a new allocation, not a new global one, so no `global_base_ptr`. - let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind)); + let alloc = M::init_allocation_extra(&self.extra, self.tcx, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); - Pointer::new(tag, Size::ZERO) + M::tag_alloc_base_pointer(self, Pointer::new(id, Size::ZERO)) } pub fn reallocate( @@ -249,7 +246,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { new_align: Align, kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { - let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?; if offset.bytes() != 0 { throw_ub_format!( "reallocating {:?} which does not point to the beginning of an object", @@ -284,7 +281,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { old_size_and_align: Option<(Size, Align)>, kind: MemoryKind, ) -> InterpResult<'tcx> { - let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?; trace!("deallocating: {}", alloc_id); if offset.bytes() != 0 { @@ -337,7 +334,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // Let the machine take some extra action let size = alloc.size(); - M::memory_deallocated(&mut self.extra, &mut alloc.extra, ptr, size)?; + M::memory_deallocated(&mut self.extra, &mut alloc.extra, ptr.provenance, alloc_range(Size::ZERO, size))?; // Don't forget to remember size and align of this now-dead allocation let old = self.dead_alloc_map.insert(alloc_id, (size, alloc.align)); @@ -424,7 +421,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { self.ptr_try_get_alloc(ptr) } else { // A "real" access, we insist on getting an `AllocId`. - Ok(self.ptr_force_alloc(ptr)?) + Ok(self.ptr_get_alloc(ptr)?) }; Ok(match ptr_or_addr { Err(addr) => { @@ -530,14 +527,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { M::before_access_global(memory_extra, id, alloc, def_id, is_write)?; let alloc = Cow::Borrowed(alloc); // We got tcx memory. Let the machine initialize its "extra" stuff. - let (alloc, tag) = M::init_allocation_extra( + let alloc = M::init_allocation_extra( memory_extra, + tcx, id, // always use the ID we got as input, not the "hidden" one. alloc, M::GLOBAL_KIND.map(MemoryKind::Machine), ); - // Sanity check that this is the same tag we would have gotten via `global_base_pointer`. - debug_assert!(tag == M::tag_global_base_pointer(memory_extra, id.into()).provenance); Ok(alloc) } @@ -596,8 +592,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { }, )?; if let Some((alloc_id, offset, ptr, alloc)) = ptr_and_alloc { - M::memory_read(&self.extra, &alloc.extra, ptr, size)?; let range = alloc_range(offset, size); + M::memory_read(&self.extra, &alloc.extra, ptr.provenance, range)?; Ok(Some(AllocRef { alloc, range, tcx: self.tcx, alloc_id })) } else { // Even in this branch we have to be sure that we actually access the allocation, in @@ -662,8 +658,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. let (alloc, extra) = self.get_raw_mut(alloc_id)?; - M::memory_written(extra, &mut alloc.extra, ptr, size)?; let range = alloc_range(offset, size); + M::memory_written(extra, &mut alloc.extra, ptr.provenance, range)?; Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id })) } else { Ok(None) @@ -756,7 +752,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &self, ptr: Pointer>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { - let (alloc_id, offset, ptr) = self.ptr_force_alloc(ptr)?; + let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())) } @@ -1036,7 +1032,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Some(src_ptr) => src_ptr, }; let src_alloc = self.get_raw(src_alloc_id)?; - M::memory_read(&self.extra, &src_alloc.extra, src, size)?; + let src_range = alloc_range(src_offset, size); + M::memory_read(&self.extra, &src_alloc.extra, src.provenance, src_range)?; // We need the `dest` ptr for the next operation, so we get it now. // We already did the source checks and called the hooks so we are good to return early. let (dest_alloc_id, dest_offset, dest) = match dest_parts { @@ -1051,23 +1048,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // relocations overlapping the edges; those would not be handled correctly). let relocations = src_alloc.prepare_relocation_copy( self, - alloc_range(src_offset, size), + src_range, dest_offset, num_copies, ); // Prepare a copy of the initialization mask. - let compressed = src_alloc.compress_uninit_range(alloc_range(src_offset, size)); + let compressed = src_alloc.compress_uninit_range(src_range); // This checks relocation edges on the src. let src_bytes = src_alloc - .get_bytes_with_uninit_and_ptr(&tcx, alloc_range(src_offset, size)) + .get_bytes_with_uninit_and_ptr(&tcx, src_range) .map_err(|e| e.to_interp_error(src_alloc_id))? .as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation // Destination alloc preparations and access hooks. let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?; - M::memory_written(extra, &mut dest_alloc.extra, dest, size * num_copies)?; + let dest_range = alloc_range(dest_offset, size * num_copies); + M::memory_written(extra, &mut dest_alloc.extra, dest.provenance, dest_range)?; let dest_bytes = dest_alloc - .get_bytes_mut_ptr(&tcx, alloc_range(dest_offset, size * num_copies)) + .get_bytes_mut_ptr(&tcx, dest_range) .as_mut_ptr(); if compressed.no_bytes_init() { @@ -1077,7 +1075,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // This also avoids writing to the target bytes so that the backing allocation is never // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary // operating system this can avoid physically allocating the page. - dest_alloc.mark_init(alloc_range(dest_offset, size * num_copies), false); // `Size` multiplication + dest_alloc.mark_init(dest_range, false); // `Size` multiplication dest_alloc.mark_relocation_range(relocations); return Ok(()); } @@ -1119,7 +1117,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // now fill in all the "init" data dest_alloc.mark_compressed_init_range( &compressed, - alloc_range(dest_offset, size), + alloc_range(dest_offset, size), // just a single copy (i.e., not full `dest_range`) num_copies, ); // copy the relocations to the destination @@ -1141,29 +1139,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } } - /// Internal helper for turning a "maybe pointer" into a proper pointer (and some information + /// Turning a "maybe pointer" into a proper pointer (and some information /// about where it points), or an absolute address. - pub(super) fn ptr_try_get_alloc( + pub fn ptr_try_get_alloc( &self, ptr: Pointer>, ) -> Result<(AllocId, Size, Pointer), u64> { - match ptr.into_pointer_or_offset() { + match ptr.into_pointer_or_addr() { Ok(ptr) => { let (alloc_id, offset) = M::ptr_get_alloc(self, ptr); - if let Some(alloc_id) = alloc_id { - Ok((alloc_id, offset, ptr)) - } else { - Err(offset.bytes()) - } + Ok((alloc_id, offset, ptr)) } - Err(offset) => Err(offset.bytes()), + Err(addr) => Err(addr.bytes()), } } - /// Internal helper for turning a "maybe pointer" into a proper pointer (and some information - /// about where it points). + /// Turning a "maybe pointer" into a proper pointer (and some information about where it points). #[inline(always)] - pub(super) fn ptr_force_alloc( + pub fn ptr_get_alloc( &self, ptr: Pointer>, ) -> InterpResult<'tcx, (AllocId, Size, Pointer)> { diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 1c9905b775f0c..5b0a940637d1e 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -199,6 +199,11 @@ impl MemPlace { MemPlace { ptr, align, meta: MemPlaceMeta::None } } + /// Adjust the provenance of the main pointer (metadata is unaffected). + pub fn map_provenance(self, f: impl FnOnce(Option) -> Option) -> Self { + MemPlace { ptr: self.ptr.map_provenance(f), ..self } + } + /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. /// This is the inverse of `ref_to_mplace`. #[inline(always)] @@ -252,7 +257,7 @@ impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { } #[inline] - fn from_aligned_ptr(ptr: Pointer>, layout: TyAndLayout<'tcx>) -> Self { + pub fn from_aligned_ptr(ptr: Pointer>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr, layout.align.abi), layout } } @@ -695,16 +700,6 @@ where Ok(place_ty) } - /// Write a scalar to a place - #[inline(always)] - pub fn write_scalar( - &mut self, - val: impl Into>, - dest: &PlaceTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx> { - self.write_immediate(Immediate::Scalar(val.into()), dest) - } - /// Write an immediate to a place #[inline(always)] pub fn write_immediate( @@ -722,21 +717,24 @@ where Ok(()) } - /// Write an `Immediate` to memory. + /// Write a scalar to a place #[inline(always)] - pub fn write_immediate_to_mplace( + pub fn write_scalar( &mut self, - src: Immediate, - dest: &MPlaceTy<'tcx, M::PointerTag>, + val: impl Into>, + dest: &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - self.write_immediate_to_mplace_no_validate(src, dest)?; - - if M::enforce_validity(self) { - // Data got changed, better make sure it matches the type! - self.validate_operand(&dest.into())?; - } + self.write_immediate(Immediate::Scalar(val.into()), dest) + } - Ok(()) + /// Write a pointer to a place + #[inline(always)] + pub fn write_pointer( + &mut self, + ptr: impl Into>>, + dest: &PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest) } /// Write an immediate to a place. diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index f0787ad0c660d..f2a8a067dfac2 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -162,9 +162,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::mir::Rvalue::*; match *rvalue { ThreadLocalRef(did) => { - let id = M::thread_local_static_alloc_id(self, did)?; - let val = self.global_base_pointer(id.into())?; - self.write_scalar(Scalar::from_pointer(val, &*self.tcx), &dest)?; + let ptr = M::thread_local_static_base_pointer(self, did)?; + self.write_pointer(ptr, &dest)?; } Use(ref operand) => { diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 73bbb38a93a93..5e2c47be3a2ea 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -869,7 +869,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let alloc = this .ecx .intern_with_temp_alloc(value.layout, |ecx, dest| { - ecx.write_immediate_to_mplace(*imm, dest) + ecx.write_immediate(*imm, dest) }) .unwrap(); Ok(Some(alloc)) From adbe7554d7fa3d0dffae9aa291607d66e6649c84 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 15 Jul 2021 18:03:22 +0200 Subject: [PATCH 11/16] enable Miri to fix the bytes in an allocation (since ptr offsets have different meanings there) --- .../src/mir/interpret/allocation.rs | 40 +++++---- compiler/rustc_mir/src/interpret/machine.rs | 13 +-- compiler/rustc_mir/src/interpret/memory.rs | 81 +++++++++---------- 3 files changed, 65 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1f2227f6ea02c..6b2d0fc1a0eb8 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use std::convert::TryFrom; use std::iter; -use std::ops::{Deref, DerefMut, Range}; +use std::ops::{Deref, Range}; use std::ptr; use rustc_ast::Mutability; @@ -156,16 +156,30 @@ impl Allocation { impl Allocation { /// Convert Tag and add Extra fields - pub fn with_prov_and_extra( + pub fn convert_tag_add_extra( self, - mut tagger: impl FnMut(AllocId) -> Tag, + cx: &impl HasDataLayout, extra: Extra, + mut tagger: impl FnMut(Pointer) -> Pointer, ) -> Allocation { + // Compute new pointer tags, which also adjusts the bytes. + let mut bytes = self.bytes; + let mut new_relocations = Vec::with_capacity(self.relocations.0.len()); + let ptr_size = cx.data_layout().pointer_size.bytes_usize(); + let endian = cx.data_layout().endian; + for &(offset, alloc_id) in self.relocations.iter() { + let idx = offset.bytes_usize(); + let ptr_bytes = &mut bytes[idx..idx + ptr_size]; + let bits = read_target_uint(endian, ptr_bytes).unwrap(); + let (ptr_tag, ptr_offset) = + tagger(Pointer::new(alloc_id, Size::from_bytes(bits))).into_parts(); + write_target_uint(endian, ptr_bytes, ptr_offset.bytes().into()).unwrap(); + new_relocations.push((offset, ptr_tag)); + } + // Create allocation. Allocation { - bytes: self.bytes, - relocations: Relocations::from_presorted( - self.relocations.iter().map(|&(offset, tag)| (offset, tagger(tag))).collect(), - ), + bytes, + relocations: Relocations::from_presorted(new_relocations), init_mask: self.init_mask, align: self.align, mutability: self.mutability, @@ -377,7 +391,7 @@ impl Allocation { // See if we have to also write a relocation. if let Some(provenance) = provenance { - self.relocations.insert(range.start, provenance); + self.relocations.0.insert(range.start, provenance); } Ok(()) @@ -437,7 +451,7 @@ impl Allocation { } // Forget all the relocations. - self.relocations.remove_range(first..last); + self.relocations.0.remove_range(first..last); } /// Errors if there are relocations overlapping with the edges of the @@ -597,12 +611,6 @@ impl Deref for Relocations { } } -impl DerefMut for Relocations { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - /// A partial, owned list of relocations to transfer into another allocation. pub struct AllocationRelocations { relative_relocations: Vec<(Size, Tag)>, @@ -643,7 +651,7 @@ impl Allocation { /// The affected range, as defined in the parameters to `prepare_relocation_copy` is expected /// to be clear of relocations. pub fn mark_relocation_range(&mut self, relocations: AllocationRelocations) { - self.relocations.insert_presorted(relocations.relative_relocations); + self.relocations.0.insert_presorted(relocations.relative_relocations); } } diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index 7b8f2aecd0d47..323e102b8723b 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use std::hash::Hash; use rustc_middle::mir; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; @@ -310,8 +310,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) fn init_allocation_extra<'b>( - memory_extra: &Self::MemoryExtra, - tcx: TyCtxt<'tcx>, + mem: &Memory<'mir, 'tcx, Self>, id: AllocId, alloc: Cow<'b, Allocation>, kind: Option>, @@ -441,8 +440,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { #[inline(always)] fn init_allocation_extra<'b>( - _memory_extra: &Self::MemoryExtra, - _tcx: TyCtxt<$tcx>, + _mem: &Memory<$mir, $tcx, Self>, _id: AllocId, alloc: Cow<'b, Allocation>, _kind: Option>, @@ -473,10 +471,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn ptr_get_alloc( - _mem: &Memory<$mir, $tcx, Self>, - ptr: Pointer, - ) -> (AllocId, Size) { + fn ptr_get_alloc(_mem: &Memory<$mir, $tcx, Self>, ptr: Pointer) -> (AllocId, Size) { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (alloc_id, offset) = ptr.into_parts(); (alloc_id, offset) diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index aae3721e4e4fb..c467665ec77b9 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -232,10 +232,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - // This is a new allocation, not a new global one, so no `global_base_ptr`. - let alloc = M::init_allocation_extra(&self.extra, self.tcx, id, Cow::Owned(alloc), Some(kind)); + let alloc = + M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); - M::tag_alloc_base_pointer(self, Pointer::new(id, Size::ZERO)) + M::tag_alloc_base_pointer(self, Pointer::from(id)) } pub fn reallocate( @@ -334,7 +334,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // Let the machine take some extra action let size = alloc.size(); - M::memory_deallocated(&mut self.extra, &mut alloc.extra, ptr.provenance, alloc_range(Size::ZERO, size))?; + M::memory_deallocated( + &mut self.extra, + &mut alloc.extra, + ptr.provenance, + alloc_range(Size::ZERO, size), + )?; // Don't forget to remember size and align of this now-dead allocation let old = self.dead_alloc_map.insert(alloc_id, (size, alloc.align)); @@ -492,12 +497,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// this machine use the same pointer tag, so it is indirected through /// `M::tag_allocation`. fn get_global_alloc( - memory_extra: &M::MemoryExtra, - tcx: TyCtxt<'tcx>, + &self, id: AllocId, is_write: bool, ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> { - let (alloc, def_id) = match tcx.get_global_alloc(id) { + let (alloc, def_id) = match self.tcx.get_global_alloc(id) { Some(GlobalAlloc::Memory(mem)) => { // Memory of a constant or promoted or anonymous memory referenced by a static. (mem, None) @@ -505,8 +509,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { - assert!(tcx.is_static(def_id)); - assert!(!tcx.is_thread_local_static(def_id)); + assert!(self.tcx.is_static(def_id)); + assert!(!self.tcx.is_thread_local_static(def_id)); // Notice that every static has two `AllocId` that will resolve to the same // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, // and the other one is maps to `GlobalAlloc::Memory`, this is returned by @@ -517,19 +521,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // The `GlobalAlloc::Memory` branch here is still reachable though; when a static // contains a reference to memory that was created during its evaluation (i.e., not // to another static), those inner references only exist in "resolved" form. - if tcx.is_foreign_item(def_id) { + if self.tcx.is_foreign_item(def_id) { throw_unsup!(ReadExternStatic(def_id)); } - (tcx.eval_static_initializer(def_id)?, Some(def_id)) + (self.tcx.eval_static_initializer(def_id)?, Some(def_id)) } }; - M::before_access_global(memory_extra, id, alloc, def_id, is_write)?; + M::before_access_global(&self.extra, id, alloc, def_id, is_write)?; let alloc = Cow::Borrowed(alloc); // We got tcx memory. Let the machine initialize its "extra" stuff. let alloc = M::init_allocation_extra( - memory_extra, - tcx, + self, id, // always use the ID we got as input, not the "hidden" one. alloc, M::GLOBAL_KIND.map(MemoryKind::Machine), @@ -548,7 +551,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.alloc_map.get_or(id, || { - let alloc = Self::get_global_alloc(&self.extra, self.tcx, id, /*is_write*/ false) + let alloc = self.get_global_alloc(id, /*is_write*/ false) .map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { @@ -619,30 +622,26 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { id: AllocId, ) -> InterpResult<'tcx, (&mut Allocation, &mut M::MemoryExtra)> { - let tcx = self.tcx; - let memory_extra = &mut self.extra; - let a = self.alloc_map.get_mut_or(id, || { - // Need to make a copy, even if `get_global_alloc` is able - // to give us a cheap reference. - let alloc = Self::get_global_alloc(memory_extra, tcx, id, /*is_write*/ true)?; + // We have "NLL problem case #3" here, which cannot be worked around without loss of + // efficiency even for the common case where the key is in the map. + // + // (Cannot use `get_mut_or` since `get_global_alloc` needs `&self`.) + if self.alloc_map.get_mut(id).is_none() { + // Slow path. + // Allocation not found locally, go look global. + let alloc = self.get_global_alloc(id, /*is_write*/ true)?; let kind = M::GLOBAL_KIND.expect( "I got a global allocation that I have to copy but the machine does \ not expect that to happen", ); - Ok((MemoryKind::Machine(kind), alloc.into_owned())) - }); - // Unpack the error type manually because type inference doesn't - // work otherwise (and we cannot help it because `impl Trait`) - match a { - Err(e) => Err(e), - Ok(a) => { - let a = &mut a.1; - if a.mutability == Mutability::Not { - throw_ub!(WriteToReadOnly(id)) - } - Ok((a, memory_extra)) - } + self.alloc_map.insert(id, (MemoryKind::Machine(kind), alloc.into_owned())); + } + + let (_kind, alloc) = self.alloc_map.get_mut(id).unwrap(); + if alloc.mutability == Mutability::Not { + throw_ub!(WriteToReadOnly(id)) } + Ok((alloc, &mut self.extra)) } /// "Safe" (bounds and align-checked) allocation access. @@ -737,7 +736,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } fn get_fn_alloc(&self, id: AllocId) -> Option> { - trace!("reading fn ptr: {}", id); if let Some(extra) = self.extra_fn_ptr_map.get(&id) { Some(FnVal::Other(*extra)) } else { @@ -752,6 +750,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { &self, ptr: Pointer>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { + trace!("get_fn({:?})", ptr); let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?; if offset.bytes() != 0 { throw_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())) @@ -1046,12 +1045,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // since we don't want to keep any relocations at the target. // (`get_bytes_with_uninit_and_ptr` below checks that there are no // relocations overlapping the edges; those would not be handled correctly). - let relocations = src_alloc.prepare_relocation_copy( - self, - src_range, - dest_offset, - num_copies, - ); + let relocations = + src_alloc.prepare_relocation_copy(self, src_range, dest_offset, num_copies); // Prepare a copy of the initialization mask. let compressed = src_alloc.compress_uninit_range(src_range); // This checks relocation edges on the src. @@ -1064,9 +1059,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?; let dest_range = alloc_range(dest_offset, size * num_copies); M::memory_written(extra, &mut dest_alloc.extra, dest.provenance, dest_range)?; - let dest_bytes = dest_alloc - .get_bytes_mut_ptr(&tcx, dest_range) - .as_mut_ptr(); + let dest_bytes = dest_alloc.get_bytes_mut_ptr(&tcx, dest_range).as_mut_ptr(); if compressed.no_bytes_init() { // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range From bd874a9d5d59e32e7e1afb6c8bac4776dc55d4d1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 15 Jul 2021 19:48:52 +0200 Subject: [PATCH 12/16] make check_ptr_access_align work on function pointer allocations --- compiler/rustc_mir/src/interpret/memory.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index c467665ec77b9..564b3429c0565 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -232,8 +232,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { M::GLOBAL_KIND.map(MemoryKind::Machine), "dynamically allocating global memory" ); - let alloc = - M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind)); + let alloc = M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind)); self.alloc_map.insert(id, (kind, alloc.into_owned())); M::tag_alloc_base_pointer(self, Pointer::from(id)) } @@ -372,7 +371,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ) } - /// Check if the given pointer is allowed to do a memory access of given `size` and `align` + /// Check if the given pointerpoints to live memory of given `size` and `align` /// (ignoring `M::enforce_alignment`). The caller can control the error message for the /// out-of-bounds case. #[inline(always)] @@ -384,7 +383,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { msg: CheckInAllocMsg, ) -> InterpResult<'tcx> { self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| { - let (size, align) = self.get_size_and_align(alloc_id, AllocCheck::Dereferenceable)?; + let check = match msg { + CheckInAllocMsg::DerefTest | CheckInAllocMsg::MemoryAccessTest => { + AllocCheck::Dereferenceable + } + CheckInAllocMsg::PointerArithmeticTest | CheckInAllocMsg::InboundsTest => { + AllocCheck::Live + } + }; + let (size, align) = self.get_size_and_align(alloc_id, check)?; Ok((size, align, ())) })?; Ok(()) @@ -551,8 +558,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // `get_global_alloc` that we can actually use directly without inserting anything anywhere. // So the error type is `InterpResult<'tcx, &Allocation>`. let a = self.alloc_map.get_or(id, || { - let alloc = self.get_global_alloc(id, /*is_write*/ false) - .map_err(Err)?; + let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?; match alloc { Cow::Borrowed(alloc) => { // We got a ref, cheaply return that as an "error" so that the From 4e280656189588a3cc30b86599a0ff4f211030b8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 15 Jul 2021 20:07:01 +0200 Subject: [PATCH 13/16] tweak pointer out-of-bounds error message --- .../rustc_middle/src/mir/interpret/error.rs | 20 ++++++++++++++----- .../ui/const-ptr/out_of_bounds_read.stderr | 6 +++--- .../consts/const-eval/ub-nonnull.32bit.stderr | 2 +- .../consts/const-eval/ub-nonnull.64bit.stderr | 2 +- .../const-eval/ub-wide-ptr.32bit.stderr | 2 +- .../const-eval/ub-wide-ptr.64bit.stderr | 2 +- src/test/ui/consts/copy-intrinsic.rs | 9 +++++---- src/test/ui/consts/copy-intrinsic.stderr | 10 +++++----- src/test/ui/consts/offset_ub.stderr | 6 +++--- src/test/ui/consts/ptr_comparisons.rs | 2 +- src/test/ui/consts/ptr_comparisons.stderr | 4 ++-- 11 files changed, 38 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8424123827662..432d078dc9b05 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -181,8 +181,8 @@ pub enum CheckInAllocMsg { } impl fmt::Display for CheckInAllocMsg { - /// When this is printed as an error the context looks like this - /// "{msg}pointer must be in-bounds at offset..." + /// When this is printed as an error the context looks like this: + /// "{msg}0x01 is not a valid pointer". fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -318,14 +318,24 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { PointerUseAfterFree(a) => { write!(f, "pointer to {} was dereferenced after this allocation got freed", a) } + PointerOutOfBounds { alloc_id, offset, size: Size::ZERO, msg, allocation_size } => { + write!( + f, + "{}{} has size {}, so pointer at offset {} is out-of-bounds", + msg, + alloc_id, + allocation_size.bytes(), + offset.bytes(), + ) + } PointerOutOfBounds { alloc_id, offset, size, msg, allocation_size } => write!( f, - "{}pointer must be in-bounds for {} bytes at offset {}, but {} has size {}", + "{}{} has size {}, so pointer to {} bytes starting at offset {} is out-of-bounds", msg, + alloc_id, + allocation_size.bytes(), size.bytes(), offset.bytes(), - alloc_id, - allocation_size.bytes() ), DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => { write!(f, "null pointer is not a valid pointer for this operation") diff --git a/src/test/ui/const-ptr/out_of_bounds_read.stderr b/src/test/ui/const-ptr/out_of_bounds_read.stderr index e6e9f483c2b3c..92541779213a4 100644 --- a/src/test/ui/const-ptr/out_of_bounds_read.stderr +++ b/src/test/ui/const-ptr/out_of_bounds_read.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 + | memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL @@ -23,7 +23,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 + | memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL @@ -47,7 +47,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { copy_nonoverlapping(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | memory access failed: pointer must be in-bounds for 4 bytes at offset 4, but alloc7 has size 4 + | memory access failed: alloc7 has size 4, so pointer to 4 bytes starting at offset 4 is out-of-bounds | inside `copy_nonoverlapping::` at $SRC_DIR/core/src/intrinsics.rs:LL:COL | ::: $SRC_DIR/core/src/ptr/mod.rs:LL:COL diff --git a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr index bebdd22397557..e44f324945481 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.32bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 + | ^^^^^^^^ dereferencing pointer failed: alloc11 has size 1, so pointer to 256 bytes starting at offset 0 is out-of-bounds error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr index 0277506eff152..1ce87bc7c1ce8 100644 --- a/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-nonnull.64bit.stderr @@ -13,7 +13,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-nonnull.rs:19:30 | LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 256 bytes at offset 0, but alloc11 has size 1 + | ^^^^^^^^ dereferencing pointer failed: alloc11 has size 1, so pointer to 256 bytes starting at offset 0 is out-of-bounds error[E0080]: it is undefined behavior to use this value --> $DIR/ub-nonnull.rs:23:1 diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr index 6609265e706b3..2a489e8b69c8f 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr @@ -302,7 +302,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/ub-wide-ptr.rs:139:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 12 bytes at offset N, but allocN has size N + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 12 bytes starting at offset N is out-of-bounds error: aborting due to 28 previous errors diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr index fc9f02400113f..ff850d2dbe9a0 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr @@ -302,7 +302,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/ub-wide-ptr.rs:139:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 24 bytes at offset N, but allocN has size N + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: allocN has size N, so pointer to 24 bytes starting at offset N is out-of-bounds error: aborting due to 28 previous errors diff --git a/src/test/ui/consts/copy-intrinsic.rs b/src/test/ui/consts/copy-intrinsic.rs index 48d128b10e802..124e16e2b2c0f 100644 --- a/src/test/ui/consts/copy-intrinsic.rs +++ b/src/test/ui/consts/copy-intrinsic.rs @@ -24,20 +24,21 @@ const COPY_OOB_1: () = unsafe { let mut x = 0i32; let dangle = (&mut x as *mut i32).wrapping_add(10); // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. - copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ evaluation of constant value failed [E0080] + copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080] + //~| pointer at offset 40 is out-of-bounds }; const COPY_OOB_2: () = unsafe { let x = 0i32; let dangle = (&x as *const i32).wrapping_add(10); // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. - copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ evaluation of constant value failed [E0080] - //~| memory access failed: pointer must be in-bounds + copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080] + //~| pointer at offset 40 is out-of-bounds }; const COPY_SIZE_OVERFLOW: () = unsafe { let x = 0; let mut y = 0; - copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); //~ evaluation of constant value failed [E0080] + copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); //~ ERROR evaluation of constant value failed [E0080] //~| overflow computing total size of `copy` }; const COPY_NONOVERLAPPING_SIZE_OVERFLOW: () = unsafe { diff --git a/src/test/ui/consts/copy-intrinsic.stderr b/src/test/ui/consts/copy-intrinsic.stderr index 4e35a8fde940c..be41c2db398be 100644 --- a/src/test/ui/consts/copy-intrinsic.stderr +++ b/src/test/ui/consts/copy-intrinsic.stderr @@ -2,22 +2,22 @@ error[E0080]: evaluation of constant value failed --> $DIR/copy-intrinsic.rs:27:5 | LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 0 bytes at offset 40, but alloc5 has size 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: alloc5 has size 4, so pointer at offset 40 is out-of-bounds error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:33:5 + --> $DIR/copy-intrinsic.rs:34:5 | LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds for 0 bytes at offset 40, but alloc7 has size 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: alloc7 has size 4, so pointer at offset 40 is out-of-bounds error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:40:5 + --> $DIR/copy-intrinsic.rs:41:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:46:5 + --> $DIR/copy-intrinsic.rs:47:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr index edd5f021d08e5..4f7c4f92060a2 100644 --- a/src/test/ui/consts/offset_ub.stderr +++ b/src/test/ui/consts/offset_ub.stderr @@ -18,7 +18,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds for 2 bytes at offset 0, but allocN has size 1 + | pointer arithmetic failed: allocN has size 1, so pointer to 2 bytes starting at offset 0 is out-of-bounds | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/offset_ub.rs:9:43 @@ -32,7 +32,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds for 101 bytes at offset 0, but allocN has size 100 + | pointer arithmetic failed: allocN has size 100, so pointer to 101 bytes starting at offset 0 is out-of-bounds | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/offset_ub.rs:10:45 @@ -102,7 +102,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds for 1 bytes at offset 0, but allocN has size 0 + | pointer arithmetic failed: allocN has size 0, so pointer to 1 bytes starting at offset 0 is out-of-bounds | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/offset_ub.rs:17:50 diff --git a/src/test/ui/consts/ptr_comparisons.rs b/src/test/ui/consts/ptr_comparisons.rs index ffe8bc98446d0..9b5fd18f818b0 100644 --- a/src/test/ui/consts/ptr_comparisons.rs +++ b/src/test/ui/consts/ptr_comparisons.rs @@ -63,7 +63,7 @@ const _: *const usize = unsafe { (FOO as *const usize).offset(2) }; const _: *const u8 = unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; //~^ ERROR evaluation of constant value failed -//~| pointer must be in-bounds +//~| out-of-bounds const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 }; //~^ ERROR any use of this value will cause an error diff --git a/src/test/ui/consts/ptr_comparisons.stderr b/src/test/ui/consts/ptr_comparisons.stderr index 032b947b895a7..6ae24c179ff1c 100644 --- a/src/test/ui/consts/ptr_comparisons.stderr +++ b/src/test/ui/consts/ptr_comparisons.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of constant value failed LL | unsafe { intrinsics::offset(self, count) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | pointer arithmetic failed: pointer must be in-bounds for $TWO_WORDS bytes at offset 0, but alloc3 has size $WORD + | pointer arithmetic failed: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds | inside `ptr::const_ptr::::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | ::: $DIR/ptr_comparisons.rs:61:34 @@ -16,7 +16,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/ptr_comparisons.rs:64:33 | LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: pointer must be in-bounds for 1000 bytes at offset 0, but alloc3 has size $WORD + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc3 has size $WORD, so pointer to 1000 bytes starting at offset 0 is out-of-bounds error: any use of this value will cause an error --> $DIR/ptr_comparisons.rs:68:27 From 7c720ce6121dd878b25f8e6a645a07563e5a4b01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jul 2021 09:39:35 +0200 Subject: [PATCH 14/16] get rid of incorrect erase_for_fmt --- .../src/mir/interpret/allocation.rs | 4 +- .../rustc_middle/src/mir/interpret/pointer.rs | 26 ++-------- .../rustc_middle/src/mir/interpret/value.rs | 52 ++++++------------- compiler/rustc_middle/src/ty/print/pretty.rs | 10 ++-- .../rustc_mir/src/interpret/eval_context.rs | 14 ++--- .../rustc_mir/src/interpret/intrinsics.rs | 2 +- compiler/rustc_mir/src/interpret/memory.rs | 14 ++--- compiler/rustc_mir/src/interpret/operand.rs | 36 ++++++++----- compiler/rustc_mir/src/interpret/place.rs | 23 -------- compiler/rustc_mir/src/interpret/validity.rs | 13 +++-- .../rustc_mir/src/transform/const_prop.rs | 4 +- .../src/thir/pattern/deconstruct_pat.rs | 2 +- 12 files changed, 81 insertions(+), 119 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 6b2d0fc1a0eb8..56b8f9b928f9d 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -377,7 +377,9 @@ impl Allocation { } }; - let (bytes, provenance) = match val.to_bits_or_ptr(range.size) { + // `to_bits_or_ptr_internal` is the right method because we just want to store this data + // as-is into memory. + let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size) { Err(val) => { let (provenance, offset) = val.into_parts(); (u128::from(offset.bytes()), Some(provenance)) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 307d7d2846208..c7daaec8d5d54 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -89,7 +89,7 @@ impl PointerArithmetic for T {} pub trait Provenance: Copy { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// If `true, ptr-to-int casts work by simply discarding the provenance. - /// If `false`, ptr-to-int casts are not supported. + /// If `false`, ptr-to-int casts are not supported. The offset *must* be relative in that case. const OFFSET_IS_ADDR: bool; /// Determines how a pointer should be printed. @@ -97,8 +97,9 @@ pub trait Provenance: Copy { where Self: Sized; - /// "Erasing" a tag converts it to the default tag type if possible. Used only for formatting purposes! - fn erase_for_fmt(self) -> AllocId; + /// Provenance must always be able to identify the allocation this ptr points to. + /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.) + fn get_alloc_id(self) -> AllocId; } impl Provenance for AllocId { @@ -120,7 +121,7 @@ impl Provenance for AllocId { Ok(()) } - fn erase_for_fmt(self) -> AllocId { + fn get_alloc_id(self) -> AllocId { self } } @@ -177,14 +178,6 @@ impl Pointer> { None => Err(self.offset), } } - - #[inline(always)] - pub fn map_erase_for_fmt(self) -> Pointer> - where - Tag: Provenance, - { - Pointer { offset: self.offset, provenance: self.provenance.map(Provenance::erase_for_fmt) } - } } impl Pointer> { @@ -208,15 +201,6 @@ impl<'tcx, Tag> Pointer { (self.provenance, self.offset) } - #[inline(always)] - pub fn erase_for_fmt(self) -> Pointer - where - Tag: Provenance, - { - // FIXME: This is wrong! `self.offset` might be an absolute address. - Pointer { offset: self.offset, provenance: self.provenance.erase_for_fmt() } - } - pub fn map_provenance(self, f: impl FnOnce(Tag) -> Tag) -> Self { Pointer { provenance: f(self.provenance), ..self } } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 1be04f8c18b1b..62b71b10d5a1b 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -289,9 +289,10 @@ impl Scalar { /// This is almost certainly not the method you want! You should dispatch on the type /// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed. /// - /// This method only exists for the benefit of low-level memory operations. + /// This method only exists for the benefit of low-level operations that truly need to treat the + /// scalar in whatever form it is. #[inline] - pub fn to_bits_or_ptr(self, target_size: Size) -> Result> { + pub fn to_bits_or_ptr_internal(self, target_size: Size) -> Result> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { Scalar::Int(int) => Ok(int.assert_bits(target_size)), @@ -304,32 +305,23 @@ impl Scalar { } impl<'tcx, Tag: Provenance> Scalar { - /// Erase the tag from the scalar, if any. - /// - /// Used by error reporting code to avoid having the error type depend on `Tag`. - #[inline] - pub fn erase_for_fmt(self) -> Scalar { - match self { - Scalar::Ptr(ptr, sz) => Scalar::Ptr(ptr.erase_for_fmt(), sz), - Scalar::Int(int) => Scalar::Int(int), - } - } - /// Fundamental scalar-to-int (cast) operation. Many convenience wrappers exist below, that you /// likely want to use instead. /// /// Will perform ptr-to-int casts if needed and possible. + /// If that fails, we know the offset is relative, so we return an "erased" Scalar + /// (which is useful for error messages but not much else). #[inline] - pub fn try_to_int(self) -> Option { + pub fn try_to_int(self) -> Result> { match self { - Scalar::Int(int) => Some(int), + Scalar::Int(int) => Ok(int), Scalar::Ptr(ptr, sz) => { if Tag::OFFSET_IS_ADDR { - Some( - ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap(), - ) + Ok(ScalarInt::try_from_uint(ptr.offset.bytes(), Size::from_bytes(sz)).unwrap()) } else { - None + // 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)) } } } @@ -340,19 +332,20 @@ impl<'tcx, Tag: Provenance> Scalar { self.try_to_int().unwrap() } + /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in + /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type. #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - self.try_to_int() - .ok_or_else(|| err_unsup!(ReadPointerAsBytes))? - .to_bits(target_size) - .map_err(|size| { + self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsBytes))?.to_bits(target_size).map_err( + |size| { err_ub!(ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes(), }) .into() - }) + }, + ) } #[inline(always)] @@ -522,17 +515,6 @@ impl ScalarMaybeUninit { } impl<'tcx, Tag: Provenance> ScalarMaybeUninit { - /// Erase the tag from the scalar, if any. - /// - /// Used by error reporting code to avoid having the error type depend on `Tag`. - #[inline] - pub fn erase_for_fmt(self) -> ScalarMaybeUninit { - match self { - ScalarMaybeUninit::Scalar(s) => ScalarMaybeUninit::Scalar(s.erase_for_fmt()), - ScalarMaybeUninit::Uninit => ScalarMaybeUninit::Uninit, - } - } - #[inline(always)] pub fn to_bool(self) -> InterpResult<'tcx, bool> { self.check_init()?.to_bool() diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index dbdca44952f45..6dfbd28f7763b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,5 +1,5 @@ use crate::middle::cstore::{ExternCrate, ExternCrateSource}; -use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Scalar}; +use crate::mir::interpret::{AllocRange, ConstValue, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::ieee::{Double, Single}; @@ -1107,9 +1107,9 @@ pub trait PrettyPrinter<'tcx>: /// This is overridden for MIR printing because we only want to hide alloc ids from users, not /// from MIR where it is actually useful. - fn pretty_print_const_pointer( + fn pretty_print_const_pointer( mut self, - _: Pointer, + _: Pointer, ty: Ty<'tcx>, print_ty: bool, ) -> Result { @@ -1680,9 +1680,9 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { } } - fn pretty_print_const_pointer( + fn pretty_print_const_pointer( self, - p: Pointer, + p: Pointer, ty: Ty<'tcx>, print_ty: bool, ) -> Result { diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index 71e4652ac0037..b130eb3ca0c39 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -897,11 +897,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty - // due to the local having ZST type. + // due to the local having ZST type. Hence we can `unwrap`. trace!( "deallocating local {:?}: {:?}", local, - self.memory.dump_alloc(ptr.provenance.unwrap().erase_for_fmt()) + self.memory.dump_alloc(ptr.provenance.unwrap().get_alloc_id()) ); self.memory.deallocate(ptr, None, MemoryKind::Stack)?; }; @@ -989,28 +989,28 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug }, mplace.ptr, )?; - allocs.extend(mplace.ptr.map_erase_for_fmt().provenance); + allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); } LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { write!(fmt, " {:?}", val)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val { - allocs.push(ptr.provenance.erase_for_fmt()); + allocs.push(ptr.provenance.get_alloc_id()); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { write!(fmt, " ({:?}, {:?})", val1, val2)?; if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val1 { - allocs.push(ptr.provenance.erase_for_fmt()); + allocs.push(ptr.provenance.get_alloc_id()); } if let ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _size)) = val2 { - allocs.push(ptr.provenance.erase_for_fmt()); + allocs.push(ptr.provenance.get_alloc_id()); } } } write!(fmt, ": {:?}", self.ecx.memory.dump_allocs(allocs)) } - Place::Ptr(mplace) => match mplace.ptr.map_erase_for_fmt().provenance { + Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) { Some(alloc_id) => write!( fmt, "by align({}) ref {:?}: {:?}", diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 9335b783c7751..dc1f9053b61f0 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -362,7 +362,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // // Control flow is weird because we cannot early-return (to reach the // `go_to_block` at the end). - let done = if let (Some(a), Some(b)) = (a.try_to_int(), b.try_to_int()) { + let done = if let (Ok(a), Ok(b)) = (a.try_to_int(), b.try_to_int()) { let a = a.try_to_machine_usize(*self.tcx).unwrap(); let b = b.try_to_machine_usize(*self.tcx).unwrap(); if a == b && a != 0 { diff --git a/compiler/rustc_mir/src/interpret/memory.rs b/compiler/rustc_mir/src/interpret/memory.rs index 564b3429c0565..b82dbf3e94b60 100644 --- a/compiler/rustc_mir/src/interpret/memory.rs +++ b/compiler/rustc_mir/src/interpret/memory.rs @@ -757,12 +757,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ptr: Pointer>, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { trace!("get_fn({:?})", ptr); - let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?; + let (alloc_id, offset, _ptr) = self.ptr_get_alloc(ptr)?; if offset.bytes() != 0 { - throw_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())) + throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))) } self.get_fn_alloc(alloc_id) - .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_for_fmt())).into()) + .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -801,7 +801,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if reachable.insert(id) { // This is a new allocation, add its relocations to `todo`. if let Some((_, alloc)) = self.alloc_map.get(id) { - todo.extend(alloc.relocations().values().map(|tag| tag.erase_for_fmt())); + todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id())); } } } @@ -841,7 +841,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.erase_for_fmt()) { + for alloc_id in alloc.relocations().values().map(|tag| tag.get_alloc_id()) { allocs_to_print.push_back(alloc_id); } write!(fmt, "{}", pretty::display_allocation(tcx, alloc)) @@ -1129,7 +1129,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { pub fn scalar_to_ptr(&self, scalar: Scalar) -> Pointer> { - match scalar.to_bits_or_ptr(self.pointer_size()) { + // We use `to_bits_or_ptr_internal` since we are just implementing the method people need to + // call to force getting out a pointer. + match scalar.to_bits_or_ptr_internal(self.pointer_size()) { Err(ptr) => ptr.into(), Ok(bits) => { let addr = u64::try_from(bits).unwrap(); diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index fefda32bfe638..04d4e3993e4f5 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -118,8 +118,14 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { ty: Ty<'tcx>, ) -> Result, std::fmt::Error> { match s { - ScalarMaybeUninit::Scalar(s) => { - cx.pretty_print_const_scalar(s.erase_for_fmt(), ty, true) + ScalarMaybeUninit::Scalar(Scalar::Int(int)) => { + cx.pretty_print_const_scalar_int(int, ty, true) + } + ScalarMaybeUninit::Scalar(Scalar::Ptr(ptr, _sz)) => { + // Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to + // print what is points to, which would fail since it has no access to the local + // memory. + cx.pretty_print_const_pointer(ptr, ty, true) } ScalarMaybeUninit::Uninit => cx.typed_value( |mut this| { @@ -139,11 +145,11 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { p(cx, s, ty)?; return Ok(()); } - write!(f, "{}: {}", s.erase_for_fmt(), self.layout.ty) + write!(f, "{}: {}", s, self.layout.ty) } Immediate::ScalarPair(a, b) => { // FIXME(oli-obk): at least print tuples and slices nicely - write!(f, "({}, {}): {}", a.erase_for_fmt(), b.erase_for_fmt(), self.layout.ty,) + write!(f, "({}, {}): {}", a, b, self.layout.ty,) } } }) @@ -693,8 +699,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(match *tag_encoding { TagEncoding::Direct => { let tag_bits = tag_val - .to_bits(tag_layout.size) - .map_err(|_| err_ub!(InvalidTag(tag_val.erase_for_fmt())))?; + .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. let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); let discr_bits = discr_val.assert_bits(discr_layout.size); @@ -711,7 +718,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_for_fmt())))?; + .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?; // Return the cast value, and the index. (discr_val, index.0) } @@ -720,18 +727,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let variant = match tag_val.to_bits_or_ptr(tag_layout.size) { - Err(ptr) => { - // The niche must be just 0 (which an inbounds pointer value never is) + let variant = match tag_val.try_to_int() { + Err(dbg_val) => { + // So this is a pointer then, and casting to an int failed. + // Can only happen during CTFE. + let ptr = self.scalar_to_ptr(tag_val); + // The niche must be just 0, and the ptr not null, then we know this is + // okay. Everything else, we conservatively reject. let ptr_valid = niche_start == 0 && variants_start == variants_end - && !self.memory.ptr_may_be_null(ptr.into()); + && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_ub!(InvalidTag(tag_val.erase_for_fmt())) + throw_ub!(InvalidTag(dbg_val)) } dataful_variant } Ok(tag_bits) => { + let tag_bits = tag_bits.assert_bits(tag_layout.size); // We need to use machine arithmetic to get the relative variant idx: // variant_index_relative = tag_val - niche_start_val let tag_val = ImmTy::from_uint(tag_bits, tag_layout); diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 5b0a940637d1e..419c17595a1b7 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -62,17 +62,6 @@ impl MemPlaceMeta { Self::None | Self::Poison => false, } } - - pub fn erase_for_fmt(self) -> MemPlaceMeta - where - Tag: Provenance, - { - match self { - Self::Meta(s) => MemPlaceMeta::Meta(s.erase_for_fmt()), - Self::None => MemPlaceMeta::None, - Self::Poison => MemPlaceMeta::Poison, - } - } } #[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] @@ -182,18 +171,6 @@ impl<'tcx, Tag> From> for PlaceTy<'tcx, Tag> { } impl MemPlace { - #[inline] - pub fn erase_for_fmt(self) -> MemPlace - where - Tag: Provenance, - { - MemPlace { - ptr: self.ptr.map_erase_for_fmt(), - align: self.align, - meta: self.meta.erase_for_fmt(), - } - } - #[inline(always)] pub fn from_ptr(ptr: Pointer>, align: Align) -> Self { MemPlace { ptr, align, meta: MemPlaceMeta::None } diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2d4eb0b823854..0c7f89c1a36ba 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -535,7 +535,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // types below! if self.ctfe_mode.is_some() { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous - let is_bits = value.check_init().map_or(false, |v| v.try_to_int().is_some()); + let is_bits = value.check_init().map_or(false, |v| v.try_to_int().is_ok()); if !is_bits { throw_validation_failure!(self.path, { "{}", value } expected { "initialized plain (non-pointer) bytes" } @@ -652,11 +652,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' err_ub!(InvalidUninitBytes(None)) => { "{}", value } expected { "something {}", wrapping_range_format(valid_range, max_hi) }, ); - let bits = match value.to_bits_or_ptr(op.layout.size) { - Err(ptr) => { + let bits = match value.try_to_int() { + Err(_) => { + // So this is a pointer then, and casting to an int failed. + // Can only happen during CTFE. + let ptr = self.ecx.scalar_to_ptr(value); if lo == 1 && hi == max_hi { // Only null is the niche. So make sure the ptr is NOT null. - if self.ecx.memory.ptr_may_be_null(ptr.into()) { + if self.ecx.memory.ptr_may_be_null(ptr) { throw_validation_failure!(self.path, { "a potentially null pointer" } expected { @@ -678,7 +681,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) } } - Ok(data) => data, + Ok(int) => int.assert_bits(op.layout.size), }; // Now compare. This is slightly subtle because this is a special "wrap-around" range. if wrapping_range_contains(&valid_range, bits) { diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 5e2c47be3a2ea..e02e41d62061a 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -921,12 +921,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { match **op { interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUninit::Scalar(s))) => { - s.try_to_int().is_some() + s.try_to_int().is_ok() } interpret::Operand::Immediate(Immediate::ScalarPair( ScalarMaybeUninit::Scalar(l), ScalarMaybeUninit::Scalar(r), - )) => l.try_to_int().is_some() && r.try_to_int().is_some(), + )) => l.try_to_int().is_ok() && r.try_to_int().is_ok(), _ => false, } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index db15f6dad3c48..ace13ea44624d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -123,7 +123,7 @@ impl IntRange { // straight to the result, after doing a bit of checking. (We // could remove this branch and just fall through, which // is more general but much slower.) - if let Ok(bits) = scalar.to_bits_or_ptr(target_size) { + if let Ok(bits) = scalar.to_bits_or_ptr_internal(target_size) { return Some(bits); } } From a5299fb6889d6eabba2b4b1a7d7e9decd49182ee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jul 2021 11:16:32 +0200 Subject: [PATCH 15/16] add some comments regarding the two major quirks of our memory model --- compiler/rustc_middle/src/mir/interpret/allocation.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 56b8f9b928f9d..bbf792edcded9 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -285,6 +285,9 @@ impl Allocation { /// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory. pub fn get_bytes_mut_ptr(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> *mut [u8] { self.mark_init(range, true); + // This also clears relocations that just overlap with the written range. So writing to some + // byte can de-initialize its neighbors! See + // for details. self.clear_relocations(cx, range); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check @@ -327,7 +330,11 @@ impl Allocation { cx: &impl HasDataLayout, range: AllocRange, ) -> AllocResult> { - // `get_bytes_unchecked` tests relocation edges. + // `get_bytes_with_uninit_and_ptr` tests relocation edges. + // We deliberately error when loading data that partially has provenance, or partially + // initialized data (that's the check below), into a scalar. The LLVM semantics of this are + // unclear so we are conservative. See for + // further discussion. let bytes = self.get_bytes_with_uninit_and_ptr(cx, range)?; // Uninit check happens *after* we established that the alignment is correct. // We must not return `Ok()` for unaligned pointers! From efbee50600e63293608811d7ee200f12641f1958 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 16 Jul 2021 19:50:59 +0200 Subject: [PATCH 16/16] avoid manual Debug impls by adding extra Provenance bounds to types I wish the derive macro would support adding extra where clauses... --- .../rustc_middle/src/mir/interpret/pointer.rs | 8 +- .../rustc_mir/src/interpret/eval_context.rs | 28 ++---- compiler/rustc_mir/src/interpret/operand.rs | 75 ++++----------- compiler/rustc_mir/src/interpret/place.rs | 92 ++++--------------- 4 files changed, 51 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index c7daaec8d5d54..7e7a7119be6a8 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -86,7 +86,9 @@ impl PointerArithmetic for T {} /// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to /// some global state. -pub trait Provenance: Copy { +/// We don't actually care about this `Debug` bound (we use `Provenance::fmt` to format the entire +/// pointer), but `derive` adds some unecessary bounds. +pub trait Provenance: Copy + fmt::Debug { /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address. /// If `true, ptr-to-int casts work by simply discarding the provenance. /// If `false`, ptr-to-int casts are not supported. The offset *must* be relative in that case. @@ -142,14 +144,14 @@ static_assert_size!(Pointer, 16); // all the Miri types. impl fmt::Debug for Pointer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Tag::fmt(self, f) + Provenance::fmt(self, f) } } impl fmt::Debug for Pointer> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.provenance { - Some(tag) => Tag::fmt(&Pointer::new(tag, self.offset), f), + Some(tag) => Provenance::fmt(&Pointer::new(tag, self.offset), f), None => write!(f, "0x{:x}", self.offset.bytes()), } } diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index b130eb3ca0c39..516ef4f4e53ca 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -80,7 +80,7 @@ impl Drop for SpanGuard { } /// A stack frame. -pub struct Frame<'mir, 'tcx, Tag = AllocId, Extra = ()> { +pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information //////////////////////////////////////////////////////////////////////////////// @@ -161,7 +161,7 @@ pub enum StackPopCleanup { /// State of a local variable including a memoized layout #[derive(Clone, PartialEq, Eq, HashStable)] -pub struct LocalState<'tcx, Tag = AllocId> { +pub struct LocalState<'tcx, Tag: Provenance = AllocId> { pub value: LocalValue, /// Don't modify if `Some`, this is only used to prevent computing the layout twice #[stable_hasher(ignore)] @@ -169,8 +169,8 @@ pub struct LocalState<'tcx, Tag = AllocId> { } /// Current value of a local variable -#[derive(Copy, Clone, PartialEq, Eq, HashStable)] -pub enum LocalValue { +#[derive(Copy, Clone, PartialEq, Eq, HashStable, Debug)] // Miri debug-prints these +pub enum LocalValue { /// This local is not currently alive, and cannot be used at all. Dead, /// This local is alive but not yet initialized. It can be written to @@ -186,19 +186,7 @@ pub enum LocalValue { Live(Operand), } -impl std::fmt::Debug for LocalValue { - // Miri debug-prints these - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use LocalValue::*; - match self { - Dead => f.debug_tuple("Dead").finish(), - Uninitialized => f.debug_tuple("Uninitialized").finish(), - Live(o) => f.debug_tuple("Live").field(o).finish(), - } - } -} - -impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { +impl<'tcx, Tag: Provenance + 'static> LocalState<'tcx, Tag> { /// Read the local's value or error if the local is not yet live or not live anymore. /// /// Note: This may only be invoked from the `Machine::access_local` hook and not from @@ -232,7 +220,7 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { } } -impl<'mir, 'tcx, Tag> Frame<'mir, 'tcx, Tag> { +impl<'mir, 'tcx, Tag: Provenance> Frame<'mir, 'tcx, Tag> { pub fn with_extra(self, extra: Extra) -> Frame<'mir, 'tcx, Tag, Extra> { Frame { body: self.body, @@ -247,7 +235,7 @@ impl<'mir, 'tcx, Tag> Frame<'mir, 'tcx, Tag> { } } -impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> { +impl<'mir, 'tcx, Tag: Provenance, Extra> Frame<'mir, 'tcx, Tag, Extra> { /// Get the current location within the Frame. /// /// If this is `Err`, we are not currently executing any particular statement in @@ -1024,7 +1012,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug } } -impl<'ctx, 'mir, 'tcx, Tag, Extra> HashStable> +impl<'ctx, 'mir, 'tcx, Tag: Provenance, Extra> HashStable> for Frame<'mir, 'tcx, Tag, Extra> where Extra: HashStable>, diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index 04d4e3993e4f5..aba7db7816843 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -27,8 +27,8 @@ use super::{ /// operations and wide pointers. This idea was taken from rustc's codegen. /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Immediate`, and do not have to work with a `Place`. -#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash)] -pub enum Immediate { +#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)] +pub enum Immediate { Scalar(ScalarMaybeUninit), ScalarPair(ScalarMaybeUninit, ScalarMaybeUninit), } @@ -36,31 +36,21 @@ pub enum Immediate { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Immediate, 56); -impl std::fmt::Debug for Immediate { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Immediate::*; - match self { - Scalar(s) => f.debug_tuple("Scalar").field(s).finish(), - ScalarPair(s1, s2) => f.debug_tuple("ScalarPair").field(s1).field(s2).finish(), - } - } -} - -impl From> for Immediate { +impl From> for Immediate { #[inline(always)] fn from(val: ScalarMaybeUninit) -> Self { Immediate::Scalar(val) } } -impl From> for Immediate { +impl From> for Immediate { #[inline(always)] fn from(val: Scalar) -> Self { Immediate::Scalar(val.into()) } } -impl<'tcx, Tag> Immediate { +impl<'tcx, Tag: Provenance> Immediate { pub fn from_pointer(p: Pointer, cx: &impl HasDataLayout) -> Self { Immediate::Scalar(ScalarMaybeUninit::from_pointer(p, cx)) } @@ -93,8 +83,8 @@ impl<'tcx, Tag> Immediate { // ScalarPair needs a type to interpret, so we often have an immediate and a type together // as input for binary and cast operations. -#[derive(Copy, Clone)] -pub struct ImmTy<'tcx, Tag = AllocId> { +#[derive(Copy, Clone, Debug)] +pub struct ImmTy<'tcx, Tag: Provenance = AllocId> { imm: Immediate, pub layout: TyAndLayout<'tcx>, } @@ -102,13 +92,6 @@ pub struct ImmTy<'tcx, Tag = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(ImmTy<'_>, 72); -impl<'tcx, Tag: Provenance> std::fmt::Debug for ImmTy<'tcx, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let ImmTy { imm, layout } = self; - f.debug_struct("ImmTy").field("imm", imm).field("layout", layout).finish() - } -} - impl std::fmt::Display for ImmTy<'tcx, Tag> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// Helper function for printing a scalar to a FmtPrinter @@ -156,7 +139,7 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { } } -impl<'tcx, Tag> std::ops::Deref for ImmTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> std::ops::Deref for ImmTy<'tcx, Tag> { type Target = Immediate; #[inline(always)] fn deref(&self) -> &Immediate { @@ -167,39 +150,22 @@ impl<'tcx, Tag> std::ops::Deref for ImmTy<'tcx, Tag> { /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. -#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash)] -pub enum Operand { +#[derive(Copy, Clone, PartialEq, Eq, HashStable, Hash, Debug)] +pub enum Operand { Immediate(Immediate), Indirect(MemPlace), } -impl std::fmt::Debug for Operand { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Operand::*; - match self { - Immediate(i) => f.debug_tuple("Immediate").field(i).finish(), - Indirect(p) => f.debug_tuple("Indirect").field(p).finish(), - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct OpTy<'tcx, Tag = AllocId> { +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct OpTy<'tcx, Tag: Provenance = AllocId> { op: Operand, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(OpTy<'_, ()>, 80); - -impl<'tcx, Tag: Provenance> std::fmt::Debug for OpTy<'tcx, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let OpTy { op, layout } = self; - f.debug_struct("OpTy").field("op", op).field("layout", layout).finish() - } -} +rustc_data_structures::static_assert_size!(OpTy<'_>, 80); -impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> std::ops::Deref for OpTy<'tcx, Tag> { type Target = Operand; #[inline(always)] fn deref(&self) -> &Operand { @@ -207,28 +173,28 @@ impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> { } } -impl<'tcx, Tag: Copy> From> for OpTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> From> for OpTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout } } } -impl<'tcx, Tag: Copy> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> From<&'_ MPlaceTy<'tcx, Tag>> for OpTy<'tcx, Tag> { #[inline(always)] fn from(mplace: &MPlaceTy<'tcx, Tag>) -> Self { OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout } } } -impl<'tcx, Tag> From> for OpTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> From> for OpTy<'tcx, Tag> { #[inline(always)] fn from(val: ImmTy<'tcx, Tag>) -> Self { OpTy { op: Operand::Immediate(val.imm), layout: val.layout } } } -impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { #[inline] pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { ImmTy { imm: val.into(), layout } @@ -259,10 +225,7 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { } #[inline] - pub fn to_const_int(self) -> ConstInt - where - Tag: Provenance, - { + pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); let int = self.to_scalar().expect("to_const_int doesn't work on scalar pairs").assert_int(); ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 419c17595a1b7..91fcc3495b1c1 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -19,9 +19,9 @@ use super::{ Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, }; -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] /// Information required for the sound usage of a `MemPlace`. -pub enum MemPlaceMeta { +pub enum MemPlaceMeta { /// The unsized payload (e.g. length for slices or vtable pointer for trait objects). Meta(Scalar), /// `Sized` types or unsized `extern type` @@ -36,18 +36,7 @@ pub enum MemPlaceMeta { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlaceMeta, 24); -impl std::fmt::Debug for MemPlaceMeta { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use MemPlaceMeta::*; - match self { - Meta(s) => f.debug_tuple("Meta").field(s).finish(), - None => f.debug_tuple("None").finish(), - Poison => f.debug_tuple("Poison").finish(), - } - } -} - -impl MemPlaceMeta { +impl MemPlaceMeta { pub fn unwrap_meta(self) -> Scalar { match self { Self::Meta(s) => s, @@ -64,8 +53,8 @@ impl MemPlaceMeta { } } -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] -pub struct MemPlace { +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] +pub struct MemPlace { /// The pointer can be a pure integer, with the `None` tag. pub ptr: Pointer>, pub align: Align, @@ -78,19 +67,8 @@ pub struct MemPlace { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MemPlace, 48); -impl std::fmt::Debug for MemPlace { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let MemPlace { ptr, align, meta } = self; - f.debug_struct("MemPlace") - .field("ptr", ptr) - .field("align", align) - .field("meta", meta) - .finish() - } -} - -#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable)] -pub enum Place { +#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)] +pub enum Place { /// A place referring to a value allocated in the `Memory` system. Ptr(MemPlace), @@ -102,20 +80,8 @@ pub enum Place { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Place, 56); -impl std::fmt::Debug for Place { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use Place::*; - match self { - Ptr(p) => f.debug_tuple("Ptr").field(p).finish(), - Local { frame, local } => { - f.debug_struct("Local").field("frame", frame).field("local", local).finish() - } - } - } -} - -#[derive(Copy, Clone)] -pub struct PlaceTy<'tcx, Tag = AllocId> { +#[derive(Copy, Clone, Debug)] +pub struct PlaceTy<'tcx, Tag: Provenance = AllocId> { place: Place, // Keep this private; it helps enforce invariants. pub layout: TyAndLayout<'tcx>, } @@ -123,14 +89,7 @@ pub struct PlaceTy<'tcx, Tag = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PlaceTy<'_>, 72); -impl<'tcx, Tag: Provenance> std::fmt::Debug for PlaceTy<'tcx, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let PlaceTy { place, layout } = self; - f.debug_struct("PlaceTy").field("place", place).field("layout", layout).finish() - } -} - -impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> std::ops::Deref for PlaceTy<'tcx, Tag> { type Target = Place; #[inline(always)] fn deref(&self) -> &Place { @@ -139,8 +98,8 @@ impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { } /// A MemPlace with its layout. Constructing it is only possible in this module. -#[derive(Copy, Clone, Hash, Eq, PartialEq)] -pub struct MPlaceTy<'tcx, Tag = AllocId> { +#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +pub struct MPlaceTy<'tcx, Tag: Provenance = AllocId> { mplace: MemPlace, pub layout: TyAndLayout<'tcx>, } @@ -148,14 +107,7 @@ pub struct MPlaceTy<'tcx, Tag = AllocId> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(MPlaceTy<'_>, 64); -impl<'tcx, Tag: Provenance> std::fmt::Debug for MPlaceTy<'tcx, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let MPlaceTy { mplace, layout } = self; - f.debug_struct("MPlaceTy").field("mplace", mplace).field("layout", layout).finish() - } -} - -impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> std::ops::Deref for MPlaceTy<'tcx, Tag> { type Target = MemPlace; #[inline(always)] fn deref(&self) -> &MemPlace { @@ -163,14 +115,14 @@ impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> { } } -impl<'tcx, Tag> From> for PlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> From> for PlaceTy<'tcx, Tag> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self { PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout } } } -impl MemPlace { +impl MemPlace { #[inline(always)] pub fn from_ptr(ptr: Pointer>, align: Align) -> Self { MemPlace { ptr, align, meta: MemPlaceMeta::None } @@ -212,7 +164,7 @@ impl MemPlace { } } -impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { /// Produces a MemPlace that works for ZST but nothing else #[inline] pub fn dangling(layout: TyAndLayout<'tcx>) -> Self { @@ -239,10 +191,7 @@ impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { } #[inline] - pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> - where - Tag: Provenance, - { + pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { // We need to consult `meta` metadata match self.layout.ty.kind() { @@ -269,7 +218,7 @@ impl<'tcx, Tag: Copy> MPlaceTy<'tcx, Tag> { } // These are defined here because they produce a place. -impl<'tcx, Tag: Copy> OpTy<'tcx, Tag> { +impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. @@ -284,10 +233,7 @@ impl<'tcx, Tag: Copy> OpTy<'tcx, Tag> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. - pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> - where - Tag: Provenance, - { + pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { self.try_as_mplace().unwrap() } }