summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir/interpret/allocation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/mir/interpret/allocation.rs')
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs71
1 files changed, 45 insertions, 26 deletions
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index b8030d9db..c787481bf 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -18,9 +18,9 @@ use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, HasDataLayout, Size};
use super::{
- read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer, Provenance,
- ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess,
- UnsupportedOpInfo,
+ read_target_uint, write_target_uint, AllocId, BadBytesAccess, InterpError, InterpResult,
+ Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch,
+ UndefinedBehaviorInfo, UnsupportedOpInfo,
};
use crate::ty;
use init_mask::*;
@@ -173,13 +173,13 @@ pub enum AllocError {
/// A scalar had the wrong size.
ScalarSizeMismatch(ScalarSizeMismatch),
/// Encountered a pointer where we needed raw bytes.
- ReadPointerAsBytes,
+ ReadPointerAsInt(Option<BadBytesAccess>),
/// Partially overwriting a pointer.
- PartialPointerOverwrite(Size),
+ OverwritePartialPointer(Size),
/// Partially copying a pointer.
- PartialPointerCopy(Size),
+ ReadPartialPointer(Size),
/// Using uninitialized data where it is not allowed.
- InvalidUninitBytes(Option<UninitBytesAccess>),
+ InvalidUninitBytes(Option<BadBytesAccess>),
}
pub type AllocResult<T = ()> = Result<T, AllocError>;
@@ -196,12 +196,14 @@ impl AllocError {
ScalarSizeMismatch(s) => {
InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s))
}
- ReadPointerAsBytes => InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes),
- PartialPointerOverwrite(offset) => InterpError::Unsupported(
- UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)),
+ ReadPointerAsInt(info) => InterpError::Unsupported(
+ UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))),
),
- PartialPointerCopy(offset) => InterpError::Unsupported(
- UnsupportedOpInfo::PartialPointerCopy(Pointer::new(alloc_id, offset)),
+ OverwritePartialPointer(offset) => InterpError::Unsupported(
+ UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)),
+ ),
+ ReadPartialPointer(offset) => InterpError::Unsupported(
+ UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)),
),
InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))),
@@ -327,6 +329,9 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
/// Try to create an Allocation of `size` bytes, panics if there is not enough memory
/// available to the compiler to do so.
+ ///
+ /// Example use case: To obtain an Allocation filled with specific data,
+ /// first call this function and then call write_scalar to fill in the right data.
pub fn uninit(size: Size, align: Align) -> Self {
match Self::uninit_inner(size, align, || {
panic!("Allocation::uninit called with panic_on_fail had allocation failure");
@@ -433,14 +438,26 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
range: AllocRange,
) -> AllocResult<&[u8]> {
self.init_mask.is_range_initialized(range).map_err(|uninit_range| {
- AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
+ AllocError::InvalidUninitBytes(Some(BadBytesAccess {
access: range,
- uninit: uninit_range,
+ bad: uninit_range,
}))
})?;
if !Prov::OFFSET_IS_ADDR {
if !self.provenance.range_empty(range, cx) {
- return Err(AllocError::ReadPointerAsBytes);
+ // Find the provenance.
+ let (offset, _prov) = self
+ .provenance
+ .range_get_ptrs(range, cx)
+ .first()
+ .copied()
+ .expect("there must be provenance somewhere here");
+ let start = offset.max(range.start); // the pointer might begin before `range`!
+ let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`!
+ return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess {
+ access: range,
+ bad: AllocRange::from(start..end),
+ })));
}
}
Ok(self.get_bytes_unchecked(range))
@@ -536,23 +553,25 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
// Now use this provenance.
let ptr = Pointer::new(prov, Size::from_bytes(bits));
return Ok(Scalar::from_maybe_pointer(ptr, cx));
+ } else {
+ // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of
+ // provenance.
+ if self.provenance.range_empty(range, cx) {
+ return Ok(Scalar::from_uint(bits, range.size));
+ }
+ // Else we have mixed provenance, that doesn't work.
+ return Err(AllocError::ReadPartialPointer(range.start));
}
} else {
// We are *not* reading a pointer.
- // If we can just ignore provenance, do exactly that.
- if Prov::OFFSET_IS_ADDR {
+ // If we can just ignore provenance or there is none, that's easy.
+ if Prov::OFFSET_IS_ADDR || self.provenance.range_empty(range, cx) {
// We just strip provenance.
return Ok(Scalar::from_uint(bits, range.size));
}
+ // There is some provenance and we don't have OFFSET_IS_ADDR. This doesn't work.
+ return Err(AllocError::ReadPointerAsInt(None));
}
-
- // Fallback path for when we cannot treat provenance bytewise or ignore it.
- assert!(!Prov::OFFSET_IS_ADDR);
- if !self.provenance.range_empty(range, cx) {
- return Err(AllocError::ReadPointerAsBytes);
- }
- // There is no provenance, we can just return the bits.
- Ok(Scalar::from_uint(bits, range.size))
}
/// Writes a *non-ZST* scalar.
@@ -571,7 +590,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
assert!(self.mutability == Mutability::Mut);
// `to_bits_or_ptr_internal` is the right method because we just want to store this data
- // as-is into memory.
+ // as-is into memory. This also double-checks that `val.size()` matches `range.size`.
let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? {
Right(ptr) => {
let (provenance, offset) = ptr.into_parts();