summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/mir/interpret
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
commitdc0db358abe19481e475e10c32149b53370f1a1c (patch)
treeab8ce99c4b255ce46f99ef402c27916055b899ee /compiler/rustc_middle/src/mir/interpret
parentReleasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff)
downloadrustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz
rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_middle/src/mir/interpret')
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs48
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs444
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs31
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs23
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs12
5 files changed, 266 insertions, 292 deletions
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 1a8e48264..b8030d9db 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -296,25 +296,13 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
Allocation::from_bytes(slice, Align::ONE, Mutability::Not)
}
- /// Try to create an Allocation of `size` bytes, failing if there is not enough memory
- /// available to the compiler to do so.
- ///
- /// If `panic_on_fail` is true, this will never return `Err`.
- pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> {
- let bytes = Bytes::zeroed(size, align).ok_or_else(|| {
- // This results in an error that can happen non-deterministically, since the memory
- // available to the compiler can change between runs. Normally queries are always
- // deterministic. However, we can be non-deterministic here because all uses of const
- // evaluation (including ConstProp!) will make compilation fail (via hard error
- // or ICE) upon encountering a `MemoryExhausted` error.
- if panic_on_fail {
- panic!("Allocation::uninit called with panic_on_fail had allocation failure")
- }
- ty::tls::with(|tcx| {
- tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
- });
- InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
- })?;
+ fn uninit_inner<R>(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result<Self, R> {
+ // This results in an error that can happen non-deterministically, since the memory
+ // available to the compiler can change between runs. Normally queries are always
+ // deterministic. However, we can be non-deterministic here because all uses of const
+ // evaluation (including ConstProp!) will make compilation fail (via hard error
+ // or ICE) upon encountering a `MemoryExhausted` error.
+ let bytes = Bytes::zeroed(size, align).ok_or_else(fail)?;
Ok(Allocation {
bytes,
@@ -325,6 +313,28 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
extra: (),
})
}
+
+ /// Try to create an Allocation of `size` bytes, failing if there is not enough memory
+ /// available to the compiler to do so.
+ pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> {
+ Self::uninit_inner(size, align, || {
+ ty::tls::with(|tcx| {
+ tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
+ });
+ InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into()
+ })
+ }
+
+ /// Try to create an Allocation of `size` bytes, panics if there is not enough memory
+ /// available to the compiler to do so.
+ 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");
+ }) {
+ Ok(x) => x,
+ Err(x) => x,
+ }
+ }
}
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 055d8e9a3..2435bc59e 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -5,11 +5,15 @@ use crate::query::TyCtxtAt;
use crate::ty::{layout, tls, Ty, ValTree};
use rustc_data_structures::sync::Lock;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+ struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
+ IntoDiagnosticArg,
+};
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
use rustc_span::def_id::DefId;
-use rustc_target::abi::{call, Align, Size};
+use rustc_target::abi::{call, Align, Size, WrappingRange};
+use std::borrow::Cow;
use std::{any::Any, backtrace::Backtrace, fmt};
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
@@ -91,21 +95,54 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
#[derive(Debug)]
struct InterpErrorInfoInner<'tcx> {
kind: InterpError<'tcx>,
+ backtrace: InterpErrorBacktrace,
+}
+
+#[derive(Debug)]
+pub struct InterpErrorBacktrace {
backtrace: Option<Box<Backtrace>>,
}
-impl fmt::Display for InterpErrorInfo<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0.kind)
+impl InterpErrorBacktrace {
+ pub fn new() -> InterpErrorBacktrace {
+ let capture_backtrace = tls::with_opt(|tcx| {
+ if let Some(tcx) = tcx {
+ *Lock::borrow(&tcx.sess.ctfe_backtrace)
+ } else {
+ CtfeBacktrace::Disabled
+ }
+ });
+
+ let backtrace = match capture_backtrace {
+ CtfeBacktrace::Disabled => None,
+ CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
+ CtfeBacktrace::Immediate => {
+ // Print it now.
+ let backtrace = Backtrace::force_capture();
+ print_backtrace(&backtrace);
+ None
+ }
+ };
+
+ InterpErrorBacktrace { backtrace }
}
-}
-impl<'tcx> InterpErrorInfo<'tcx> {
pub fn print_backtrace(&self) {
- if let Some(backtrace) = self.0.backtrace.as_ref() {
+ if let Some(backtrace) = self.backtrace.as_ref() {
print_backtrace(backtrace);
}
}
+}
+
+impl<'tcx> InterpErrorInfo<'tcx> {
+ pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
+ Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
+ }
+
+ pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
+ let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
+ (kind, backtrace)
+ }
pub fn into_kind(self) -> InterpError<'tcx> {
let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
@@ -130,32 +167,17 @@ impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
fn from(kind: InterpError<'tcx>) -> Self {
- let capture_backtrace = tls::with_opt(|tcx| {
- if let Some(tcx) = tcx {
- *Lock::borrow(&tcx.sess.ctfe_backtrace)
- } else {
- CtfeBacktrace::Disabled
- }
- });
-
- let backtrace = match capture_backtrace {
- CtfeBacktrace::Disabled => None,
- CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
- CtfeBacktrace::Immediate => {
- // Print it now.
- let backtrace = Backtrace::force_capture();
- print_backtrace(&backtrace);
- None
- }
- };
-
- InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
+ InterpErrorInfo(Box::new(InterpErrorInfoInner {
+ kind,
+ backtrace: InterpErrorBacktrace::new(),
+ }))
}
}
/// Error information for when the program we executed turned out not to actually be a valid
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
/// where we work on generic code or execution does not have all information available.
+#[derive(Debug)]
pub enum InvalidProgramInfo<'tcx> {
/// Resolution can fail if we are in a too generic context.
TooGeneric,
@@ -174,25 +196,6 @@ pub enum InvalidProgramInfo<'tcx> {
UninitUnsizedLocal,
}
-impl fmt::Display for InvalidProgramInfo<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use InvalidProgramInfo::*;
- match self {
- TooGeneric => write!(f, "encountered overly generic constant"),
- AlreadyReported(_) => {
- write!(
- f,
- "an error has already been reported elsewhere (this should not usually be printed)"
- )
- }
- Layout(ref err) => write!(f, "{err}"),
- FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
- SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
- UninitUnsizedLocal => write!(f, "unsized local is used while uninitialized"),
- }
- }
-}
-
/// Details of why a pointer had to be in-bounds.
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum CheckInAllocMsg {
@@ -208,26 +211,25 @@ pub enum CheckInAllocMsg {
InboundsTest,
}
-impl fmt::Display for CheckInAllocMsg {
- /// When this is printed as an error the context looks like this:
- /// "{msg}{pointer} is a dangling pointer".
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{}",
- match *self {
- CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
- CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
- CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ",
- CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
- CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ",
- }
- )
+#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
+pub enum InvalidMetaKind {
+ /// Size of a `[T]` is too big
+ SliceTooBig,
+ /// Size of a DST is too big
+ TooBig,
+}
+
+impl IntoDiagnosticArg for InvalidMetaKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Borrowed(match self {
+ InvalidMetaKind::SliceTooBig => "slice_too_big",
+ InvalidMetaKind::TooBig => "too_big",
+ }))
}
}
/// Details of an access to uninitialized bytes where it is not allowed.
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub struct UninitBytesAccess {
/// Range of the original memory access.
pub access: AllocRange,
@@ -242,17 +244,32 @@ pub struct ScalarSizeMismatch {
pub data_size: u64,
}
+macro_rules! impl_into_diagnostic_arg_through_debug {
+ ($($ty:ty),*$(,)?) => {$(
+ impl IntoDiagnosticArg for $ty {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
+ }
+ }
+ )*}
+}
+
+// These types have nice `Debug` output so we can just use them in diagnostics.
+impl_into_diagnostic_arg_through_debug! {
+ AllocId,
+ Pointer,
+ AllocRange,
+}
+
/// Error information for when the program caused Undefined Behavior.
-pub enum UndefinedBehaviorInfo {
- /// Free-form case. Only for errors that are never caught!
+#[derive(Debug)]
+pub enum UndefinedBehaviorInfo<'a> {
+ /// Free-form case. Only for errors that are never caught! Used by miri
Ub(String),
/// Unreachable code was executed.
Unreachable,
/// A slice/array index projection went out-of-bounds.
- BoundsCheckFailed {
- len: u64,
- index: u64,
- },
+ BoundsCheckFailed { len: u64, index: u64 },
/// Something was divided by 0 (x / 0).
DivisionByZero,
/// Something was "remainded" by 0 (x % 0).
@@ -263,8 +280,8 @@ pub enum UndefinedBehaviorInfo {
RemainderOverflow,
/// Overflowing inbounds pointer arithmetic.
PointerArithOverflow,
- /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
- InvalidMeta(&'static str),
+ /// Invalid metadata in a wide pointer
+ InvalidMeta(InvalidMetaKind),
/// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed.
@@ -281,25 +298,13 @@ pub enum UndefinedBehaviorInfo {
/// Using an integer as a pointer in the wrong way.
DanglingIntPointer(u64, CheckInAllocMsg),
/// Used a pointer with bad alignment.
- AlignmentCheckFailed {
- required: Align,
- has: Align,
- },
+ AlignmentCheckFailed { required: Align, has: Align },
/// Writing to read-only memory.
WriteToReadOnly(AllocId),
- // Trying to access the data behind a function pointer.
+ /// Trying to access the data behind a function pointer.
DerefFunctionPointer(AllocId),
- // Trying to access the data behind a vtable pointer.
+ /// Trying to access the data behind a vtable pointer.
DerefVTablePointer(AllocId),
- /// The value validity check found a problem.
- /// Should only be thrown by `validity.rs` and always point out which part of the value
- /// is the problem.
- ValidationFailure {
- /// The "path" to the value in question, e.g. `.0[5].field` for a struct
- /// field in the 6th element of an array that is the first element of a tuple.
- path: Option<String>,
- msg: String,
- },
/// Using a non-boolean `u8` as bool.
InvalidBool(u8),
/// Using a non-character `u32` as character.
@@ -320,110 +325,100 @@ pub enum UndefinedBehaviorInfo {
ScalarSizeMismatch(ScalarSizeMismatch),
/// A discriminant of an uninhabited enum variant is written.
UninhabitedEnumVariantWritten,
+ /// Validation error.
+ Validation(ValidationErrorInfo<'a>),
+ // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
+ // dispatched
+ /// A custom (free-form) error, created by `err_ub_custom!`.
+ Custom(crate::error::CustomSubdiagnostic<'a>),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum PointerKind {
+ Ref,
+ Box,
+}
+
+impl IntoDiagnosticArg for PointerKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(
+ match self {
+ Self::Ref => "ref",
+ Self::Box => "box",
+ }
+ .into(),
+ )
+ }
}
-impl fmt::Display for UndefinedBehaviorInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use UndefinedBehaviorInfo::*;
- match self {
- Ub(msg) => write!(f, "{msg}"),
- Unreachable => write!(f, "entering unreachable code"),
- BoundsCheckFailed { ref len, ref index } => {
- write!(f, "indexing out of bounds: the len is {len} but the index is {index}")
- }
- DivisionByZero => write!(f, "dividing by zero"),
- RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
- DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
- RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
- PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
- InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
- UnterminatedCString(p) => write!(
- f,
- "reading a null-terminated string starting at {p:?} with no null found before end of allocation",
- ),
- PointerUseAfterFree(a) => {
- write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
- }
- PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
- write!(
- f,
- "{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
- alloc_size = alloc_size.bytes(),
- )
- }
- PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
- f,
- "{msg}{alloc_id:?} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
- alloc_size = alloc_size.bytes(),
- ptr_size = ptr_size.bytes(),
- ptr_size_p = pluralize!(ptr_size.bytes()),
- ),
- DanglingIntPointer(i, msg) => {
- write!(
- f,
- "{msg}{pointer} is a dangling pointer (it has no provenance)",
- pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
- )
- }
- AlignmentCheckFailed { required, has } => write!(
- f,
- "accessing memory with alignment {has}, but alignment {required} is required",
- has = has.bytes(),
- required = required.bytes()
- ),
- WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
- DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
- DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
- ValidationFailure { path: None, msg } => {
- write!(f, "constructing invalid value: {msg}")
- }
- ValidationFailure { path: Some(path), msg } => {
- write!(f, "constructing invalid value at {path}: {msg}")
- }
- InvalidBool(b) => {
- write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
- }
- InvalidChar(c) => {
- write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
- }
- InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
- InvalidFunctionPointer(p) => {
- write!(f, "using {p:?} as function pointer but it does not point to a function")
- }
- InvalidVTablePointer(p) => {
- write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
- }
- InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
- InvalidUninitBytes(Some((alloc, info))) => write!(
- f,
- "reading memory at {alloc:?}{access:?}, \
- but memory is uninitialized at {uninit:?}, \
- and this operation requires initialized memory",
- access = info.access,
- uninit = info.uninit,
- ),
- InvalidUninitBytes(None) => write!(
- f,
- "using uninitialized data, but this operation requires initialized memory"
- ),
- DeadLocal => write!(f, "accessing a dead local variable"),
- ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
- f,
- "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
- ),
- UninhabitedEnumVariantWritten => {
- write!(f, "writing discriminant of an uninhabited enum")
- }
+#[derive(Debug)]
+pub struct ValidationErrorInfo<'tcx> {
+ pub path: Option<String>,
+ pub kind: ValidationErrorKind<'tcx>,
+}
+
+#[derive(Debug)]
+pub enum ExpectedKind {
+ Reference,
+ Box,
+ RawPtr,
+ InitScalar,
+ Bool,
+ Char,
+ Float,
+ Int,
+ FnPtr,
+}
+
+impl From<PointerKind> for ExpectedKind {
+ fn from(x: PointerKind) -> ExpectedKind {
+ match x {
+ PointerKind::Box => ExpectedKind::Box,
+ PointerKind::Ref => ExpectedKind::Reference,
}
}
}
+#[derive(Debug)]
+pub enum ValidationErrorKind<'tcx> {
+ PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
+ PtrToStatic { ptr_kind: PointerKind },
+ PtrToMut { ptr_kind: PointerKind },
+ ExpectedNonPtr { value: String },
+ MutableRefInConst,
+ NullFnPtr,
+ NeverVal,
+ NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
+ PtrOutOfRange { range: WrappingRange, max_value: u128 },
+ OutOfRange { value: String, range: WrappingRange, max_value: u128 },
+ UnsafeCell,
+ UninhabitedVal { ty: Ty<'tcx> },
+ InvalidEnumTag { value: String },
+ UninitEnumTag,
+ UninitStr,
+ Uninit { expected: ExpectedKind },
+ UninitVal,
+ InvalidVTablePtr { value: String },
+ InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
+ InvalidMetaTooLarge { ptr_kind: PointerKind },
+ UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
+ NullPtr { ptr_kind: PointerKind },
+ DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
+ DanglingPtrOutOfBounds { ptr_kind: PointerKind },
+ DanglingPtrUseAfterFree { ptr_kind: PointerKind },
+ InvalidBool { value: String },
+ InvalidChar { value: String },
+ InvalidFnPtr { value: String },
+}
+
/// Error information for when the program did something that might (or might not) be correct
/// to do according to the Rust spec, but due to limitations in the interpreter, the
/// operation could not be carried out. These limitations can differ between CTFE and the
/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
+#[derive(Debug)]
pub enum UnsupportedOpInfo {
/// Free-form case. Only for errors that are never caught!
+ // FIXME still use translatable diagnostics
Unsupported(String),
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
@@ -442,83 +437,42 @@ pub enum UnsupportedOpInfo {
ReadExternStatic(DefId),
}
-impl fmt::Display for UnsupportedOpInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use UnsupportedOpInfo::*;
- match self {
- Unsupported(ref msg) => write!(f, "{msg}"),
- PartialPointerOverwrite(ptr) => {
- write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
- }
- PartialPointerCopy(ptr) => {
- write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
- }
- ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
- ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
- ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
- }
- }
-}
-
/// Error information for when the program exhausted the resources granted to it
/// by the interpreter.
+#[derive(Debug)]
pub enum ResourceExhaustionInfo {
/// The stack grew too big.
StackFrameLimitReached,
- /// The program ran for too long.
- ///
- /// The exact limit is set by the `const_eval_limit` attribute.
- StepLimitReached,
/// There is not enough memory (on the host) to perform an allocation.
MemoryExhausted,
/// The address space (of the target) is full.
AddressSpaceFull,
}
-impl fmt::Display for ResourceExhaustionInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use ResourceExhaustionInfo::*;
- match self {
- StackFrameLimitReached => {
- write!(f, "reached the configured maximum number of stack frames")
- }
- StepLimitReached => {
- write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
- }
- MemoryExhausted => {
- write!(f, "tried to allocate more memory than available to compiler")
- }
- AddressSpaceFull => {
- write!(f, "there are no more free addresses in the address space")
- }
- }
- }
-}
-
-/// A trait to work around not having trait object upcasting.
-pub trait AsAny: Any {
- fn as_any(&self) -> &dyn Any;
-}
-impl<T: Any> AsAny for T {
- #[inline(always)]
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
/// A trait for machine-specific errors (or other "machine stop" conditions).
-pub trait MachineStopType: AsAny + fmt::Display + Send {}
+pub trait MachineStopType: Any + fmt::Debug + Send {
+ /// The diagnostic message for this error
+ fn diagnostic_message(&self) -> DiagnosticMessage;
+ /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
+ /// fluent for formatting the translated diagnostic message.
+ fn add_args(
+ self: Box<Self>,
+ adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
+ );
+}
impl dyn MachineStopType {
#[inline(always)]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
- self.as_any().downcast_ref()
+ let x: &dyn Any = self;
+ x.downcast_ref()
}
}
+#[derive(Debug)]
pub enum InterpError<'tcx> {
/// The program caused undefined behavior.
- UndefinedBehavior(UndefinedBehaviorInfo),
+ UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
/// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo),
@@ -534,26 +488,6 @@ pub enum InterpError<'tcx> {
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
-impl fmt::Display for InterpError<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use InterpError::*;
- match *self {
- Unsupported(ref msg) => write!(f, "{msg}"),
- InvalidProgram(ref msg) => write!(f, "{msg}"),
- UndefinedBehavior(ref msg) => write!(f, "{msg}"),
- ResourceExhaustion(ref msg) => write!(f, "{msg}"),
- MachineStop(ref msg) => write!(f, "{msg}"),
- }
- }
-}
-
-// Forward `Debug` to `Display`, so it does not look awful.
-impl fmt::Debug for InterpError<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, f)
- }
-}
-
impl InterpError<'_> {
/// Some errors do string formatting even if the error is never printed.
/// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
@@ -562,7 +496,7 @@ impl InterpError<'_> {
matches!(
self,
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
- | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
+ | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
)
}
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 3620385fa..2d2cfee1b 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -89,6 +89,30 @@ macro_rules! throw_machine_stop {
($($tt:tt)*) => { do yeet err_machine_stop!($($tt)*) };
}
+#[macro_export]
+macro_rules! err_ub_custom {
+ ($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{
+ $(
+ let ($($name,)*) = ($($value,)*);
+ )?
+ err_ub!(Custom(
+ rustc_middle::error::CustomSubdiagnostic {
+ msg: || $msg,
+ add_args: Box::new(move |mut set_arg| {
+ $($(
+ set_arg(stringify!($name).into(), rustc_errors::IntoDiagnosticArg::into_diagnostic_arg($name));
+ )*)?
+ })
+ }
+ ))
+ }};
+}
+
+#[macro_export]
+macro_rules! throw_ub_custom {
+ ($($tt:tt)*) => { do yeet err_ub_custom!($($tt)*) };
+}
+
mod allocation;
mod error;
mod pointer;
@@ -119,9 +143,10 @@ use crate::ty::{self, Instance, Ty, TyCtxt};
pub use self::error::{
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
- EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo,
- MachineStopType, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch,
- UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
+ EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
+ InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
+ ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
+ ValidationErrorInfo, ValidationErrorKind,
};
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index f53dc8cb0..9c97431f3 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -95,8 +95,15 @@ impl<'tcx> TyCtxt<'tcx> {
// used generic parameters is a bug of evaluation, so checking for it
// here does feel somewhat sensible.
if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
- assert!(matches!(self.def_kind(ct.def), DefKind::AnonConst));
- let mir_body = self.mir_for_ctfe(ct.def);
+ let def_kind = self.def_kind(instance.def_id());
+ assert!(
+ matches!(
+ def_kind,
+ DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
+ ),
+ "{cid:?} is {def_kind:?}",
+ );
+ let mir_body = self.mir_for_ctfe(instance.def_id());
if mir_body.is_polymorphic {
let Some(local_def_id) = ct.def.as_local() else { return };
self.struct_span_lint_hir(
@@ -236,15 +243,3 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
self.eval_to_allocation_raw(param_env.and(gid))
}
}
-
-impl<'tcx> TyCtxt<'tcx> {
- /// Destructure a mir constant ADT or array into its variant index and its field values.
- /// Panics if the destructuring fails, use `try_destructure_mir_constant` for fallible version.
- pub fn destructure_mir_constant(
- self,
- param_env: ty::ParamEnv<'tcx>,
- constant: mir::ConstantKind<'tcx>,
- ) -> mir::DestructuredConstant<'tcx> {
- self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
- }
-}
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 36dbbe4bf..0416411df 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -97,6 +97,10 @@ impl<'tcx> ConstValue<'tcx> {
ConstValue::Scalar(Scalar::from_u64(i))
}
+ pub fn from_u128(i: u128) -> Self {
+ ConstValue::Scalar(Scalar::from_u128(i))
+ }
+
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
ConstValue::Scalar(Scalar::from_target_usize(i, cx))
}
@@ -241,6 +245,11 @@ impl<Prov> Scalar<Prov> {
}
#[inline]
+ pub fn from_u128(i: u128) -> Self {
+ Scalar::Int(i.into())
+ }
+
+ #[inline]
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
Self::from_uint(i, cx.data_layout().pointer_size)
}
@@ -375,7 +384,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_bits(self, target_size: Size) -> u128 {
- self.to_bits(target_size).unwrap()
+ self.to_bits(target_size)
+ .unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}"))
}
pub fn to_bool(self) -> InterpResult<'tcx, bool> {