1 use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::{layout, query::TyCtxtAt, tls, Ty, ValTree};
6 use rustc_data_structures::sync::Lock;
7 use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
8 use rustc_macros::HashStable;
9 use rustc_session::CtfeBacktrace;
10 use rustc_span::def_id::DefId;
11 use rustc_target::abi::{call, Align, Size};
12 use std::{any::Any, backtrace::Backtrace, fmt};
14 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
15 pub enum ErrorHandled {
16 /// Already reported an error for this evaluation, and the compilation is
17 /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
18 Reported(ErrorGuaranteed),
19 /// Don't emit an error, the evaluation failed because the MIR was generic
20 /// and the substs didn't fully monomorphize it.
24 impl From<ErrorGuaranteed> for ErrorHandled {
25 fn from(err: ErrorGuaranteed) -> ErrorHandled {
26 ErrorHandled::Reported(err)
30 TrivialTypeTraversalAndLiftImpls! {
34 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
35 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
36 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
38 pub fn struct_error<'tcx>(
41 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
42 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
45 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
46 static_assert_size!(InterpErrorInfo<'_>, 8);
48 /// Packages the kind of error we got from the const code interpreter
49 /// up with a Rust-level backtrace of where the error occurred.
50 /// These should always be constructed by calling `.into()` on
51 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
54 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
57 struct InterpErrorInfoInner<'tcx> {
58 kind: InterpError<'tcx>,
59 backtrace: Option<Box<Backtrace>>,
62 impl fmt::Display for InterpErrorInfo<'_> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "{}", self.0.kind)
68 impl<'tcx> InterpErrorInfo<'tcx> {
69 pub fn print_backtrace(&self) {
70 if let Some(backtrace) = self.0.backtrace.as_ref() {
71 print_backtrace(backtrace);
75 pub fn into_kind(self) -> InterpError<'tcx> {
76 let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
81 pub fn kind(&self) -> &InterpError<'tcx> {
86 fn print_backtrace(backtrace: &Backtrace) {
87 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
90 impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
91 fn from(err: ErrorGuaranteed) -> Self {
92 InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
96 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
97 fn from(kind: InterpError<'tcx>) -> Self {
98 let capture_backtrace = tls::with_opt(|tcx| {
99 if let Some(tcx) = tcx {
100 *Lock::borrow(&tcx.sess.ctfe_backtrace)
102 CtfeBacktrace::Disabled
106 let backtrace = match capture_backtrace {
107 CtfeBacktrace::Disabled => None,
108 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
109 CtfeBacktrace::Immediate => {
111 let backtrace = Backtrace::force_capture();
112 print_backtrace(&backtrace);
117 InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
121 /// Error information for when the program we executed turned out not to actually be a valid
122 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
123 /// where we work on generic code or execution does not have all information available.
124 pub enum InvalidProgramInfo<'tcx> {
125 /// Resolution can fail if we are in a too generic context.
127 /// Abort in case errors are already reported.
128 AlreadyReported(ErrorGuaranteed),
129 /// An error occurred during layout computation.
130 Layout(layout::LayoutError<'tcx>),
131 /// An error occurred during FnAbi computation: the passed --target lacks FFI support
132 /// (which unfortunately typeck does not reject).
133 /// Not using `FnAbiError` as that contains a nested `LayoutError`.
134 FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
135 /// SizeOf of unsized type was requested.
136 SizeOfUnsizedType(Ty<'tcx>),
139 impl fmt::Display for InvalidProgramInfo<'_> {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 use InvalidProgramInfo::*;
143 TooGeneric => write!(f, "encountered overly generic constant"),
144 AlreadyReported(ErrorGuaranteed { .. }) => {
147 "an error has already been reported elsewhere (this should not usually be printed)"
150 Layout(ref err) => write!(f, "{err}"),
151 FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
152 SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
157 /// Details of why a pointer had to be in-bounds.
158 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
159 pub enum CheckInAllocMsg {
160 /// We are dereferencing a pointer (i.e., creating a place).
162 /// We are access memory.
164 /// We are doing pointer arithmetic.
165 PointerArithmeticTest,
166 /// We are doing pointer offset_from.
168 /// None of the above -- generic/unspecific inbounds test.
172 impl fmt::Display for CheckInAllocMsg {
173 /// When this is printed as an error the context looks like this:
174 /// "{msg}{pointer} is a dangling pointer".
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
181 CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
182 CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ",
183 CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
184 CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ",
190 /// Details of an access to uninitialized bytes where it is not allowed.
192 pub struct UninitBytesAccess {
193 /// Range of the original memory access.
194 pub access: AllocRange,
195 /// Range of the uninit memory that was encountered. (Might not be maximal.)
196 pub uninit: AllocRange,
199 /// Information about a size mismatch.
201 pub struct ScalarSizeMismatch {
202 pub target_size: u64,
206 /// Error information for when the program caused Undefined Behavior.
207 pub enum UndefinedBehaviorInfo {
208 /// Free-form case. Only for errors that are never caught!
210 /// Unreachable code was executed.
212 /// A slice/array index projection went out-of-bounds.
217 /// Something was divided by 0 (x / 0).
219 /// Something was "remainded" by 0 (x % 0).
221 /// Signed division overflowed (INT_MIN / -1).
223 /// Signed remainder overflowed (INT_MIN % -1).
225 /// Overflowing inbounds pointer arithmetic.
226 PointerArithOverflow,
227 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
228 InvalidMeta(&'static str),
229 /// Reading a C string that does not end within its allocation.
230 UnterminatedCString(Pointer),
231 /// Dereferencing a dangling pointer after it got freed.
232 PointerUseAfterFree(AllocId),
233 /// Used a pointer outside the bounds it is valid for.
234 /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
240 msg: CheckInAllocMsg,
242 /// Using an integer as a pointer in the wrong way.
243 DanglingIntPointer(u64, CheckInAllocMsg),
244 /// Used a pointer with bad alignment.
245 AlignmentCheckFailed {
249 /// Writing to read-only memory.
250 WriteToReadOnly(AllocId),
251 // Trying to access the data behind a function pointer.
252 DerefFunctionPointer(AllocId),
253 // Trying to access the data behind a vtable pointer.
254 DerefVTablePointer(AllocId),
255 /// The value validity check found a problem.
256 /// Should only be thrown by `validity.rs` and always point out which part of the value
259 /// The "path" to the value in question, e.g. `.0[5].field` for a struct
260 /// field in the 6th element of an array that is the first element of a tuple.
261 path: Option<String>,
264 /// Using a non-boolean `u8` as bool.
266 /// Using a non-character `u32` as character.
268 /// The tag of an enum does not encode an actual discriminant.
270 /// Using a pointer-not-to-a-function as function pointer.
271 InvalidFunctionPointer(Pointer),
272 /// Using a pointer-not-to-a-vtable as vtable pointer.
273 InvalidVTablePointer(Pointer),
274 /// Using a string that is not valid UTF-8,
275 InvalidStr(std::str::Utf8Error),
276 /// Using uninitialized data where it is not allowed.
277 InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
278 /// Working with a local that is not currently live.
280 /// Data size is not equal to target size.
281 ScalarSizeMismatch(ScalarSizeMismatch),
282 /// A discriminant of an uninhabited enum variant is written.
283 UninhabitedEnumVariantWritten,
286 impl fmt::Display for UndefinedBehaviorInfo {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 use UndefinedBehaviorInfo::*;
290 Ub(msg) => write!(f, "{msg}"),
291 Unreachable => write!(f, "entering unreachable code"),
292 BoundsCheckFailed { ref len, ref index } => {
293 write!(f, "indexing out of bounds: the len is {len} but the index is {index}")
295 DivisionByZero => write!(f, "dividing by zero"),
296 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
297 DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
298 RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
299 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
300 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
301 UnterminatedCString(p) => write!(
303 "reading a null-terminated string starting at {p:?} with no null found before end of allocation",
305 PointerUseAfterFree(a) => {
306 write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
308 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
311 "{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
312 alloc_size = alloc_size.bytes(),
315 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
317 "{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",
318 alloc_size = alloc_size.bytes(),
319 ptr_size = ptr_size.bytes(),
320 ptr_size_p = pluralize!(ptr_size.bytes()),
322 DanglingIntPointer(i, msg) => {
325 "{msg}{pointer} is a dangling pointer (it has no provenance)",
326 pointer = Pointer::<Option<AllocId>>::from_addr(*i),
329 AlignmentCheckFailed { required, has } => write!(
331 "accessing memory with alignment {has}, but alignment {required} is required",
333 required = required.bytes()
335 WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
336 DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
337 DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
338 ValidationFailure { path: None, msg } => {
339 write!(f, "constructing invalid value: {msg}")
341 ValidationFailure { path: Some(path), msg } => {
342 write!(f, "constructing invalid value at {path}: {msg}")
345 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
348 write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
350 InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
351 InvalidFunctionPointer(p) => {
352 write!(f, "using {p:?} as function pointer but it does not point to a function")
354 InvalidVTablePointer(p) => {
355 write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
357 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
358 InvalidUninitBytes(Some((alloc, info))) => write!(
360 "reading memory at {alloc:?}{access:?}, \
361 but memory is uninitialized at {uninit:?}, \
362 and this operation requires initialized memory",
363 access = info.access,
364 uninit = info.uninit,
366 InvalidUninitBytes(None) => write!(
368 "using uninitialized data, but this operation requires initialized memory"
370 DeadLocal => write!(f, "accessing a dead local variable"),
371 ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
373 "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
375 UninhabitedEnumVariantWritten => {
376 write!(f, "writing discriminant of an uninhabited enum")
382 /// Error information for when the program did something that might (or might not) be correct
383 /// to do according to the Rust spec, but due to limitations in the interpreter, the
384 /// operation could not be carried out. These limitations can differ between CTFE and the
385 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
386 pub enum UnsupportedOpInfo {
387 /// Free-form case. Only for errors that are never caught!
390 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
392 /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
393 /// cannot be represented by the CTFE interpreter.
394 PartialPointerOverwrite(Pointer<AllocId>),
395 /// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute
396 /// addresses, the resulting state cannot be represented by the CTFE interpreter.
397 PartialPointerCopy(Pointer<AllocId>),
398 /// Encountered a pointer where we needed raw bytes.
400 /// Accessing thread local statics
401 ThreadLocalStatic(DefId),
402 /// Accessing an unsupported extern static.
403 ReadExternStatic(DefId),
406 impl fmt::Display for UnsupportedOpInfo {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 use UnsupportedOpInfo::*;
410 Unsupported(ref msg) => write!(f, "{msg}"),
411 PartialPointerOverwrite(ptr) => {
412 write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
414 PartialPointerCopy(ptr) => {
415 write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
417 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
418 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
419 ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
424 /// Error information for when the program exhausted the resources granted to it
425 /// by the interpreter.
426 pub enum ResourceExhaustionInfo {
427 /// The stack grew too big.
428 StackFrameLimitReached,
429 /// The program ran for too long.
431 /// The exact limit is set by the `const_eval_limit` attribute.
433 /// There is not enough memory to perform an allocation.
437 impl fmt::Display for ResourceExhaustionInfo {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 use ResourceExhaustionInfo::*;
441 StackFrameLimitReached => {
442 write!(f, "reached the configured maximum number of stack frames")
444 StepLimitReached => {
445 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
448 write!(f, "tried to allocate more memory than available to compiler")
454 /// A trait to work around not having trait object upcasting.
455 pub trait AsAny: Any {
456 fn as_any(&self) -> &dyn Any;
458 impl<T: Any> AsAny for T {
460 fn as_any(&self) -> &dyn Any {
465 /// A trait for machine-specific errors (or other "machine stop" conditions).
466 pub trait MachineStopType: AsAny + fmt::Display + Send {}
468 impl dyn MachineStopType {
470 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
471 self.as_any().downcast_ref()
475 pub enum InterpError<'tcx> {
476 /// The program caused undefined behavior.
477 UndefinedBehavior(UndefinedBehaviorInfo),
478 /// The program did something the interpreter does not support (some of these *might* be UB
479 /// but the interpreter is not sure).
480 Unsupported(UnsupportedOpInfo),
481 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
482 InvalidProgram(InvalidProgramInfo<'tcx>),
483 /// The program exhausted the interpreter's resources (stack/heap too big,
484 /// execution takes too long, ...).
485 ResourceExhaustion(ResourceExhaustionInfo),
486 /// Stop execution for a machine-controlled reason. This is never raised by
487 /// the core engine itself.
488 MachineStop(Box<dyn MachineStopType>),
491 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
493 impl fmt::Display for InterpError<'_> {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 Unsupported(ref msg) => write!(f, "{msg}"),
498 InvalidProgram(ref msg) => write!(f, "{msg}"),
499 UndefinedBehavior(ref msg) => write!(f, "{msg}"),
500 ResourceExhaustion(ref msg) => write!(f, "{msg}"),
501 MachineStop(ref msg) => write!(f, "{msg}"),
506 // Forward `Debug` to `Display`, so it does not look awful.
507 impl fmt::Debug for InterpError<'_> {
508 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509 fmt::Display::fmt(self, f)
513 impl InterpError<'_> {
514 /// Some errors do string formatting even if the error is never printed.
515 /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
516 /// so this method lets us detect them and `bug!` on unexpected errors.
517 pub fn formatted_string(&self) -> bool {
520 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
521 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
522 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))