1 use super::{AllocId, Pointer, RawConst, 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, ErrorReported};
8 use rustc_macros::HashStable;
9 use rustc_session::CtfeBacktrace;
10 use rustc_span::def_id::DefId;
11 use rustc_target::abi::{Align, Size};
12 use std::{any::Any, backtrace::Backtrace, fmt, mem};
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(ErrorReported),
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<ErrorReported> for ErrorHandled {
27 fn from(err: ErrorReported) -> ErrorHandled {
28 ErrorHandled::Reported(err)
32 CloneTypeFoldableAndLiftImpls! {
36 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
37 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
39 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
40 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
43 /// Packages the kind of error we got from the const code interpreter
44 /// up with a Rust-level backtrace of where the error occurred.
45 /// Thsese should always be constructed by calling `.into()` on
46 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
49 pub struct InterpErrorInfo<'tcx> {
50 pub kind: InterpError<'tcx>,
51 backtrace: Option<Box<Backtrace>>,
54 impl fmt::Display for InterpErrorInfo<'_> {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "{}", self.kind)
60 impl InterpErrorInfo<'_> {
61 pub fn print_backtrace(&self) {
62 if let Some(backtrace) = self.backtrace.as_ref() {
63 print_backtrace(backtrace);
68 fn print_backtrace(backtrace: &Backtrace) {
69 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
72 impl From<ErrorHandled> for InterpErrorInfo<'_> {
73 fn from(err: ErrorHandled) -> Self {
75 ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
76 err_inval!(ReferencedConstant)
78 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
84 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
85 fn from(kind: InterpError<'tcx>) -> Self {
86 let capture_backtrace = tls::with_opt(|tcx| {
87 if let Some(tcx) = tcx {
88 *Lock::borrow(&tcx.sess.ctfe_backtrace)
90 CtfeBacktrace::Disabled
94 let backtrace = match capture_backtrace {
95 CtfeBacktrace::Disabled => None,
96 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
97 CtfeBacktrace::Immediate => {
99 let backtrace = Backtrace::force_capture();
100 print_backtrace(&backtrace);
105 InterpErrorInfo { kind, backtrace }
109 /// Error information for when the program we executed turned out not to actually be a valid
110 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
111 /// where we work on generic code or execution does not have all information available.
112 pub enum InvalidProgramInfo<'tcx> {
113 /// Resolution can fail if we are in a too generic context.
115 /// Cannot compute this constant because it depends on another one
116 /// which already produced an error.
118 /// Abort in case type errors are reached.
119 TypeckError(ErrorReported),
120 /// An error occurred during layout computation.
121 Layout(layout::LayoutError<'tcx>),
122 /// An invalid transmute happened.
123 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
126 impl fmt::Display for InvalidProgramInfo<'_> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 use InvalidProgramInfo::*;
130 TooGeneric => write!(f, "encountered overly generic constant"),
131 ReferencedConstant => write!(f, "referenced constant has errors"),
132 TypeckError(ErrorReported) => {
133 write!(f, "encountered constants with type errors, stopping evaluation")
135 Layout(ref err) => write!(f, "{}", err),
136 TransmuteSizeDiff(from_ty, to_ty) => write!(
138 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
145 /// Details of why a pointer had to be in-bounds.
146 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
147 pub enum CheckInAllocMsg {
150 PointerArithmeticTest,
154 impl fmt::Display for CheckInAllocMsg {
155 /// When this is printed as an error the context looks like this
156 /// "{test name} failed: pointer must be in-bounds at offset..."
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 CheckInAllocMsg::MemoryAccessTest => "memory access",
163 CheckInAllocMsg::NullPointerTest => "NULL pointer test",
164 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic",
165 CheckInAllocMsg::InboundsTest => "inbounds test",
171 /// Details of an access to uninitialized bytes where it is not allowed.
173 pub struct UninitBytesAccess {
174 /// Location of the original memory access.
175 pub access_ptr: Pointer,
176 /// Size of the original memory access.
177 pub access_size: Size,
178 /// Location of the first uninitialized byte that was accessed.
179 pub uninit_ptr: Pointer,
180 /// Number of consecutive uninitialized bytes that were accessed.
181 pub uninit_size: Size,
184 /// Error information for when the program caused Undefined Behavior.
185 pub enum UndefinedBehaviorInfo<'tcx> {
186 /// Free-form case. Only for errors that are never caught!
188 /// Unreachable code was executed.
190 /// A slice/array index projection went out-of-bounds.
195 /// Something was divided by 0 (x / 0).
197 /// Something was "remainded" by 0 (x % 0).
199 /// Overflowing inbounds pointer arithmetic.
200 PointerArithOverflow,
201 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
202 InvalidMeta(&'static str),
203 /// Invalid drop function in vtable.
204 InvalidDropFn(FnSig<'tcx>),
205 /// Reading a C string that does not end within its allocation.
206 UnterminatedCString(Pointer),
207 /// Dereferencing a dangling pointer after it got freed.
208 PointerUseAfterFree(AllocId),
209 /// Used a pointer outside the bounds it is valid for.
212 msg: CheckInAllocMsg,
213 allocation_size: Size,
215 /// Using an integer as a pointer in the wrong way.
216 DanglingIntPointer(u64, CheckInAllocMsg),
217 /// Used a pointer with bad alignment.
218 AlignmentCheckFailed {
222 /// Writing to read-only memory.
223 WriteToReadOnly(AllocId),
224 // Trying to access the data behind a function pointer.
225 DerefFunctionPointer(AllocId),
226 /// The value validity check found a problem.
227 /// Should only be thrown by `validity.rs` and always point out which part of the value
229 ValidationFailure(String),
230 /// Using a non-boolean `u8` as bool.
232 /// Using a non-character `u32` as character.
234 /// The tag of an enum does not encode an actual discriminant.
236 /// Using a pointer-not-to-a-function as function pointer.
237 InvalidFunctionPointer(Pointer),
238 /// Using a string that is not valid UTF-8,
239 InvalidStr(std::str::Utf8Error),
240 /// Using uninitialized data where it is not allowed.
241 InvalidUninitBytes(Option<Box<UninitBytesAccess>>),
242 /// Working with a local that is not currently live.
244 /// Data size is not equal to target size.
251 impl fmt::Display for UndefinedBehaviorInfo<'_> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 use UndefinedBehaviorInfo::*;
255 Ub(msg) => write!(f, "{}", msg),
256 Unreachable => write!(f, "entering unreachable code"),
257 BoundsCheckFailed { ref len, ref index } => {
258 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
260 DivisionByZero => write!(f, "dividing by zero"),
261 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
262 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
263 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
264 InvalidDropFn(sig) => write!(
266 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
269 UnterminatedCString(p) => write!(
271 "reading a null-terminated string starting at {} with no null found before end of allocation",
274 PointerUseAfterFree(a) => {
275 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
277 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
279 "{} failed: pointer must be in-bounds at offset {}, \
280 but is outside bounds of {} which has size {}",
284 allocation_size.bytes()
286 DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => {
287 write!(f, "NULL pointer is not allowed for this operation")
289 DanglingIntPointer(i, msg) => {
290 write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i)
292 AlignmentCheckFailed { required, has } => write!(
294 "accessing memory with alignment {}, but alignment {} is required",
298 WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
299 DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
300 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
302 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
305 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
307 InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
308 InvalidFunctionPointer(p) => {
309 write!(f, "using {} as function pointer but it does not point to a function", p)
311 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
312 InvalidUninitBytes(Some(access)) => write!(
314 "reading {} byte{} of memory starting at {}, \
315 but {} byte{} {} uninitialized starting at {}, \
316 and this operation requires initialized memory",
317 access.access_size.bytes(),
318 pluralize!(access.access_size.bytes()),
320 access.uninit_size.bytes(),
321 pluralize!(access.uninit_size.bytes()),
322 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
325 InvalidUninitBytes(None) => write!(
327 "using uninitialized data, but this operation requires initialized memory"
329 DeadLocal => write!(f, "accessing a dead local variable"),
330 ScalarSizeMismatch { target_size, data_size } => write!(
332 "scalar size mismatch: expected {} bytes but got {} bytes instead",
333 target_size, data_size
339 /// Error information for when the program did something that might (or might not) be correct
340 /// to do according to the Rust spec, but due to limitations in the interpreter, the
341 /// operation could not be carried out. These limitations can differ between CTFE and the
342 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
343 pub enum UnsupportedOpInfo {
344 /// Free-form case. Only for errors that are never caught!
346 /// Could not find MIR for a function.
348 /// Encountered a pointer where we needed raw bytes.
351 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
353 /// Encountered raw bytes where we needed a pointer.
355 /// Accessing thread local statics
356 ThreadLocalStatic(DefId),
357 /// Accessing an unsupported extern static.
358 ReadExternStatic(DefId),
361 impl fmt::Display for UnsupportedOpInfo {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 use UnsupportedOpInfo::*;
365 Unsupported(ref msg) => write!(f, "{}", msg),
366 ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
367 NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
368 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
369 ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
370 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
375 /// Error information for when the program exhausted the resources granted to it
376 /// by the interpreter.
377 pub enum ResourceExhaustionInfo {
378 /// The stack grew too big.
379 StackFrameLimitReached,
380 /// The program ran for too long.
382 /// The exact limit is set by the `const_eval_limit` attribute.
386 impl fmt::Display for ResourceExhaustionInfo {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 use ResourceExhaustionInfo::*;
390 StackFrameLimitReached => {
391 write!(f, "reached the configured maximum number of stack frames")
393 StepLimitReached => {
394 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
400 /// A trait to work around not having trait object upcasting.
401 pub trait AsAny: Any {
402 fn as_any(&self) -> &dyn Any;
404 impl<T: Any> AsAny for T {
406 fn as_any(&self) -> &dyn Any {
411 /// A trait for machine-specific errors (or other "machine stop" conditions).
412 pub trait MachineStopType: AsAny + fmt::Display + Send {}
413 impl MachineStopType for String {}
415 impl dyn MachineStopType {
417 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
418 self.as_any().downcast_ref()
422 #[cfg(target_arch = "x86_64")]
423 static_assert_size!(InterpError<'_>, 40);
425 pub enum InterpError<'tcx> {
426 /// The program caused undefined behavior.
427 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
428 /// The program did something the interpreter does not support (some of these *might* be UB
429 /// but the interpreter is not sure).
430 Unsupported(UnsupportedOpInfo),
431 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
432 InvalidProgram(InvalidProgramInfo<'tcx>),
433 /// The program exhausted the interpreter's resources (stack/heap too big,
434 /// execution takes too long, ...).
435 ResourceExhaustion(ResourceExhaustionInfo),
436 /// Stop execution for a machine-controlled reason. This is never raised by
437 /// the core engine itself.
438 MachineStop(Box<dyn MachineStopType>),
441 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
443 impl fmt::Display for InterpError<'_> {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 Unsupported(ref msg) => write!(f, "{}", msg),
448 InvalidProgram(ref msg) => write!(f, "{}", msg),
449 UndefinedBehavior(ref msg) => write!(f, "{}", msg),
450 ResourceExhaustion(ref msg) => write!(f, "{}", msg),
451 MachineStop(ref msg) => write!(f, "{}", msg),
456 // Forward `Debug` to `Display`, so it does not look awful.
457 impl fmt::Debug for InterpError<'_> {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 fmt::Display::fmt(self, f)
463 impl InterpError<'_> {
464 /// Some errors allocate to be created as they contain free-form strings.
465 /// And sometimes we want to be sure that did not happen as it is a
466 /// waste of resources.
467 pub fn allocates(&self) -> bool {
469 // Zero-sized boxes do not allocate.
470 InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
471 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
472 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
473 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
474 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => {