1 use super::{AllocId, ConstAlloc, Pointer, Scalar};
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
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 /// Already emitted a lint for this evaluation.
21 /// Don't emit an error, the evaluation failed because the MIR was generic
22 /// and the substs didn't fully monomorphize it.
26 impl From<ErrorGuaranteed> for ErrorHandled {
27 fn from(err: ErrorGuaranteed) -> ErrorHandled {
28 ErrorHandled::Reported(err)
32 TrivialTypeFoldableAndLiftImpls! {
36 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
37 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
39 pub fn struct_error<'tcx>(
42 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
43 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
46 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
47 static_assert_size!(InterpErrorInfo<'_>, 8);
49 /// Packages the kind of error we got from the const code interpreter
50 /// up with a Rust-level backtrace of where the error occurred.
51 /// These should always be constructed by calling `.into()` on
52 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
55 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
58 struct InterpErrorInfoInner<'tcx> {
59 kind: InterpError<'tcx>,
60 backtrace: Option<Box<Backtrace>>,
63 impl fmt::Display for InterpErrorInfo<'_> {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{}", self.0.kind)
69 impl<'tcx> InterpErrorInfo<'tcx> {
70 pub fn print_backtrace(&self) {
71 if let Some(backtrace) = self.0.backtrace.as_ref() {
72 print_backtrace(backtrace);
76 pub fn into_kind(self) -> InterpError<'tcx> {
77 let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
82 pub fn kind(&self) -> &InterpError<'tcx> {
87 fn print_backtrace(backtrace: &Backtrace) {
88 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
91 impl From<ErrorHandled> for InterpErrorInfo<'_> {
92 fn from(err: ErrorHandled) -> Self {
94 ErrorHandled::Reported(ErrorGuaranteed) | ErrorHandled::Linted => {
95 err_inval!(ReferencedConstant)
97 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
103 impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
104 fn from(err: ErrorGuaranteed) -> Self {
105 InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
109 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
110 fn from(kind: InterpError<'tcx>) -> Self {
111 let capture_backtrace = tls::with_opt(|tcx| {
112 if let Some(tcx) = tcx {
113 *Lock::borrow(&tcx.sess.ctfe_backtrace)
115 CtfeBacktrace::Disabled
119 let backtrace = match capture_backtrace {
120 CtfeBacktrace::Disabled => None,
121 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
122 CtfeBacktrace::Immediate => {
124 let backtrace = Backtrace::force_capture();
125 print_backtrace(&backtrace);
130 InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
134 /// Error information for when the program we executed turned out not to actually be a valid
135 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
136 /// where we work on generic code or execution does not have all information available.
137 pub enum InvalidProgramInfo<'tcx> {
138 /// Resolution can fail if we are in a too generic context.
140 /// Cannot compute this constant because it depends on another one
141 /// which already produced an error.
143 /// Abort in case errors are already reported.
144 AlreadyReported(ErrorGuaranteed),
145 /// An error occurred during layout computation.
146 Layout(layout::LayoutError<'tcx>),
147 /// An error occurred during FnAbi computation: the passed --target lacks FFI support
148 /// (which unfortunately typeck does not reject).
149 /// Not using `FnAbiError` as that contains a nested `LayoutError`.
150 FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
151 /// An invalid transmute happened.
152 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
153 /// SizeOf of unsized type was requested.
154 SizeOfUnsizedType(Ty<'tcx>),
157 impl fmt::Display for InvalidProgramInfo<'_> {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 use InvalidProgramInfo::*;
161 TooGeneric => write!(f, "encountered overly generic constant"),
162 ReferencedConstant => write!(f, "referenced constant has errors"),
163 AlreadyReported(ErrorGuaranteed) => {
164 write!(f, "encountered constants with type errors, stopping evaluation")
166 Layout(ref err) => write!(f, "{}", err),
167 FnAbiAdjustForForeignAbi(ref err) => write!(f, "{}", err),
168 TransmuteSizeDiff(from_ty, to_ty) => write!(
170 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
173 SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty),
178 /// Details of why a pointer had to be in-bounds.
179 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
180 pub enum CheckInAllocMsg {
181 /// We are dereferencing a pointer (i.e., creating a place).
183 /// We are access memory.
185 /// We are doing pointer arithmetic.
186 PointerArithmeticTest,
187 /// None of the above -- generic/unspecific inbounds test.
191 impl fmt::Display for CheckInAllocMsg {
192 /// When this is printed as an error the context looks like this:
193 /// "{msg}0x01 is not a valid pointer".
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
200 CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
201 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
202 CheckInAllocMsg::InboundsTest => "",
208 /// Details of an access to uninitialized bytes where it is not allowed.
210 pub struct UninitBytesAccess {
211 /// Location of the original memory access.
212 pub access_offset: Size,
213 /// Size of the original memory access.
214 pub access_size: Size,
215 /// Location of the first uninitialized byte that was accessed.
216 pub uninit_offset: Size,
217 /// Number of consecutive uninitialized bytes that were accessed.
218 pub uninit_size: Size,
221 /// Error information for when the program caused Undefined Behavior.
222 pub enum UndefinedBehaviorInfo<'tcx> {
223 /// Free-form case. Only for errors that are never caught!
225 /// Unreachable code was executed.
227 /// A slice/array index projection went out-of-bounds.
232 /// Something was divided by 0 (x / 0).
234 /// Something was "remainded" by 0 (x % 0).
236 /// Signed division overflowed (INT_MIN / -1).
238 /// Signed remainder overflowed (INT_MIN % -1).
240 /// Overflowing inbounds pointer arithmetic.
241 PointerArithOverflow,
242 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
243 InvalidMeta(&'static str),
244 /// Invalid drop function in vtable.
245 InvalidVtableDropFn(FnSig<'tcx>),
246 /// Invalid size in a vtable: too large.
248 /// Invalid alignment in a vtable: too large, or not a power of 2.
249 InvalidVtableAlignment(String),
250 /// Reading a C string that does not end within its allocation.
251 UnterminatedCString(Pointer),
252 /// Dereferencing a dangling pointer after it got freed.
253 PointerUseAfterFree(AllocId),
254 /// Used a pointer outside the bounds it is valid for.
255 /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
261 msg: CheckInAllocMsg,
263 /// Using an integer as a pointer in the wrong way.
264 DanglingIntPointer(u64, CheckInAllocMsg),
265 /// Used a pointer with bad alignment.
266 AlignmentCheckFailed {
270 /// Writing to read-only memory.
271 WriteToReadOnly(AllocId),
272 // Trying to access the data behind a function pointer.
273 DerefFunctionPointer(AllocId),
274 /// The value validity check found a problem.
275 /// Should only be thrown by `validity.rs` and always point out which part of the value
278 /// The "path" to the value in question, e.g. `.0[5].field` for a struct
279 /// field in the 6th element of an array that is the first element of a tuple.
280 path: Option<String>,
283 /// Using a non-boolean `u8` as bool.
285 /// Using a non-character `u32` as character.
287 /// The tag of an enum does not encode an actual discriminant.
289 /// Using a pointer-not-to-a-function as function pointer.
290 InvalidFunctionPointer(Pointer),
291 /// Using a string that is not valid UTF-8,
292 InvalidStr(std::str::Utf8Error),
293 /// Using uninitialized data where it is not allowed.
294 InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
295 /// Working with a local that is not currently live.
297 /// Data size is not equal to target size.
302 /// A discriminant of an uninhabited enum variant is written.
303 UninhabitedEnumVariantWritten,
306 impl fmt::Display for UndefinedBehaviorInfo<'_> {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 use UndefinedBehaviorInfo::*;
310 Ub(msg) => write!(f, "{}", msg),
311 Unreachable => write!(f, "entering unreachable code"),
312 BoundsCheckFailed { ref len, ref index } => {
313 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
315 DivisionByZero => write!(f, "dividing by zero"),
316 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
317 DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
318 RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
319 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
320 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
321 InvalidVtableDropFn(sig) => write!(
323 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
326 InvalidVtableSize => {
327 write!(f, "invalid vtable: size is bigger than largest supported object")
329 InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
330 UnterminatedCString(p) => write!(
332 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
335 PointerUseAfterFree(a) => {
336 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
338 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
341 "{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
344 alloc_size = alloc_size.bytes(),
345 ptr_offset = ptr_offset,
348 PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
350 "{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
353 alloc_size = alloc_size.bytes(),
354 ptr_size = ptr_size.bytes(),
355 ptr_size_p = pluralize!(ptr_size.bytes()),
356 ptr_offset = ptr_offset,
358 DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
359 write!(f, "null pointer is not a valid pointer for this operation")
361 DanglingIntPointer(i, msg) => {
362 write!(f, "{}0x{:x} is not a valid pointer", msg, i)
364 AlignmentCheckFailed { required, has } => write!(
366 "accessing memory with alignment {}, but alignment {} is required",
370 WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
371 DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
372 ValidationFailure { path: None, msg } => write!(f, "type validation failed: {}", msg),
373 ValidationFailure { path: Some(path), msg } => {
374 write!(f, "type validation failed at {}: {}", path, msg)
377 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
380 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
382 InvalidTag(val) => write!(f, "enum value has invalid tag: {:x}", val),
383 InvalidFunctionPointer(p) => {
384 write!(f, "using {:?} as function pointer but it does not point to a function", p)
386 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
387 InvalidUninitBytes(Some((alloc, access))) => write!(
389 "reading {} byte{} of memory starting at {:?}, \
390 but {} byte{} {} uninitialized starting at {:?}, \
391 and this operation requires initialized memory",
392 access.access_size.bytes(),
393 pluralize!(access.access_size.bytes()),
394 Pointer::new(*alloc, access.access_offset),
395 access.uninit_size.bytes(),
396 pluralize!(access.uninit_size.bytes()),
397 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
398 Pointer::new(*alloc, access.uninit_offset),
400 InvalidUninitBytes(None) => write!(
402 "using uninitialized data, but this operation requires initialized memory"
404 DeadLocal => write!(f, "accessing a dead local variable"),
405 ScalarSizeMismatch { target_size, data_size } => write!(
407 "scalar size mismatch: expected {} bytes but got {} bytes instead",
408 target_size, data_size
410 UninhabitedEnumVariantWritten => {
411 write!(f, "writing discriminant of an uninhabited enum")
417 /// Error information for when the program did something that might (or might not) be correct
418 /// to do according to the Rust spec, but due to limitations in the interpreter, the
419 /// operation could not be carried out. These limitations can differ between CTFE and the
420 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
421 pub enum UnsupportedOpInfo {
422 /// Free-form case. Only for errors that are never caught!
424 /// Encountered a pointer where we needed raw bytes.
426 /// Overwriting parts of a pointer; the resulting state cannot be represented in our
427 /// `Allocation` data structure.
428 PartialPointerOverwrite(Pointer<AllocId>),
430 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
432 /// Accessing thread local statics
433 ThreadLocalStatic(DefId),
434 /// Accessing an unsupported extern static.
435 ReadExternStatic(DefId),
438 impl fmt::Display for UnsupportedOpInfo {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 use UnsupportedOpInfo::*;
442 Unsupported(ref msg) => write!(f, "{}", msg),
443 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
444 PartialPointerOverwrite(ptr) => {
445 write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr)
447 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
448 ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
453 /// Error information for when the program exhausted the resources granted to it
454 /// by the interpreter.
455 pub enum ResourceExhaustionInfo {
456 /// The stack grew too big.
457 StackFrameLimitReached,
458 /// The program ran for too long.
460 /// The exact limit is set by the `const_eval_limit` attribute.
462 /// There is not enough memory to perform an allocation.
466 impl fmt::Display for ResourceExhaustionInfo {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 use ResourceExhaustionInfo::*;
470 StackFrameLimitReached => {
471 write!(f, "reached the configured maximum number of stack frames")
473 StepLimitReached => {
474 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
477 write!(f, "tried to allocate more memory than available to compiler")
483 /// A trait to work around not having trait object upcasting.
484 pub trait AsAny: Any {
485 fn as_any(&self) -> &dyn Any;
487 impl<T: Any> AsAny for T {
489 fn as_any(&self) -> &dyn Any {
494 /// A trait for machine-specific errors (or other "machine stop" conditions).
495 pub trait MachineStopType: AsAny + fmt::Display + Send {
496 /// If `true`, emit a hard error instead of going through the `CONST_ERR` lint
497 fn is_hard_err(&self) -> bool {
502 impl dyn MachineStopType {
504 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
505 self.as_any().downcast_ref()
509 pub enum InterpError<'tcx> {
510 /// The program caused undefined behavior.
511 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
512 /// The program did something the interpreter does not support (some of these *might* be UB
513 /// but the interpreter is not sure).
514 Unsupported(UnsupportedOpInfo),
515 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
516 InvalidProgram(InvalidProgramInfo<'tcx>),
517 /// The program exhausted the interpreter's resources (stack/heap too big,
518 /// execution takes too long, ...).
519 ResourceExhaustion(ResourceExhaustionInfo),
520 /// Stop execution for a machine-controlled reason. This is never raised by
521 /// the core engine itself.
522 MachineStop(Box<dyn MachineStopType>),
525 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
527 impl fmt::Display for InterpError<'_> {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
531 Unsupported(ref msg) => write!(f, "{}", msg),
532 InvalidProgram(ref msg) => write!(f, "{}", msg),
533 UndefinedBehavior(ref msg) => write!(f, "{}", msg),
534 ResourceExhaustion(ref msg) => write!(f, "{}", msg),
535 MachineStop(ref msg) => write!(f, "{}", msg),
540 // Forward `Debug` to `Display`, so it does not look awful.
541 impl fmt::Debug for InterpError<'_> {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 fmt::Display::fmt(self, f)
547 impl InterpError<'_> {
548 /// Some errors do string formatting even if the error is never printed.
549 /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
550 /// so this method lets us detect them and `bug!` on unexpected errors.
551 pub fn formatted_string(&self) -> bool {
554 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
555 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
556 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
560 /// Should this error be reported as a hard error, preventing compilation, or a soft error,
561 /// causing a deny-by-default lint?
562 pub fn is_hard_err(&self) -> bool {
565 MachineStop(ref err) => err.is_hard_err(),
566 UndefinedBehavior(_) => true,
567 ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) => true,