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, 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};
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 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>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
40 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
43 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
44 static_assert_size!(InterpErrorInfo<'_>, 8);
46 /// Packages the kind of error we got from the const code interpreter
47 /// up with a Rust-level backtrace of where the error occurred.
48 /// These should always be constructed by calling `.into()` on
49 /// a `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
52 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
55 struct InterpErrorInfoInner<'tcx> {
56 kind: InterpError<'tcx>,
57 backtrace: Option<Box<Backtrace>>,
60 impl fmt::Display for InterpErrorInfo<'_> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "{}", self.0.kind)
66 impl InterpErrorInfo<'tcx> {
67 pub fn print_backtrace(&self) {
68 if let Some(backtrace) = self.0.backtrace.as_ref() {
69 print_backtrace(backtrace);
73 pub fn into_kind(self) -> InterpError<'tcx> {
74 let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
79 pub fn kind(&self) -> &InterpError<'tcx> {
84 fn print_backtrace(backtrace: &Backtrace) {
85 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
88 impl From<ErrorHandled> for InterpErrorInfo<'_> {
89 fn from(err: ErrorHandled) -> Self {
91 ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
92 err_inval!(ReferencedConstant)
94 ErrorHandled::TooGeneric => err_inval!(TooGeneric),
100 impl From<ErrorReported> for InterpErrorInfo<'_> {
101 fn from(err: ErrorReported) -> Self {
102 InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
106 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
107 fn from(kind: InterpError<'tcx>) -> Self {
108 let capture_backtrace = tls::with_opt(|tcx| {
109 if let Some(tcx) = tcx {
110 *Lock::borrow(&tcx.sess.ctfe_backtrace)
112 CtfeBacktrace::Disabled
116 let backtrace = match capture_backtrace {
117 CtfeBacktrace::Disabled => None,
118 CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
119 CtfeBacktrace::Immediate => {
121 let backtrace = Backtrace::force_capture();
122 print_backtrace(&backtrace);
127 InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
131 /// Error information for when the program we executed turned out not to actually be a valid
132 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
133 /// where we work on generic code or execution does not have all information available.
134 pub enum InvalidProgramInfo<'tcx> {
135 /// Resolution can fail if we are in a too generic context.
137 /// Cannot compute this constant because it depends on another one
138 /// which already produced an error.
140 /// Abort in case errors are already reported.
141 AlreadyReported(ErrorReported),
142 /// An error occurred during layout computation.
143 Layout(layout::LayoutError<'tcx>),
144 /// An invalid transmute happened.
145 TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
146 /// SizeOf of unsized type was requested.
147 SizeOfUnsizedType(Ty<'tcx>),
150 impl fmt::Display for InvalidProgramInfo<'_> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 use InvalidProgramInfo::*;
154 TooGeneric => write!(f, "encountered overly generic constant"),
155 ReferencedConstant => write!(f, "referenced constant has errors"),
156 AlreadyReported(ErrorReported) => {
157 write!(f, "encountered constants with type errors, stopping evaluation")
159 Layout(ref err) => write!(f, "{}", err),
160 TransmuteSizeDiff(from_ty, to_ty) => write!(
162 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
165 SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty),
170 /// Details of why a pointer had to be in-bounds.
171 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
172 pub enum CheckInAllocMsg {
173 /// We are access memory.
175 /// We are doing pointer arithmetic.
176 PointerArithmeticTest,
177 /// None of the above -- generic/unspecific inbounds test.
181 impl fmt::Display for CheckInAllocMsg {
182 /// When this is printed as an error the context looks like this
183 /// "{msg}pointer must be in-bounds at offset..."
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
190 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
191 CheckInAllocMsg::InboundsTest => "",
197 /// Details of an access to uninitialized bytes where it is not allowed.
199 pub struct UninitBytesAccess {
200 /// Location of the original memory access.
201 pub access_offset: Size,
202 /// Size of the original memory access.
203 pub access_size: Size,
204 /// Location of the first uninitialized byte that was accessed.
205 pub uninit_offset: Size,
206 /// Number of consecutive uninitialized bytes that were accessed.
207 pub uninit_size: Size,
210 /// Error information for when the program caused Undefined Behavior.
211 pub enum UndefinedBehaviorInfo<'tcx> {
212 /// Free-form case. Only for errors that are never caught!
214 /// Unreachable code was executed.
216 /// A slice/array index projection went out-of-bounds.
221 /// Something was divided by 0 (x / 0).
223 /// Something was "remainded" by 0 (x % 0).
225 /// Overflowing inbounds pointer arithmetic.
226 PointerArithOverflow,
227 /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
228 InvalidMeta(&'static str),
229 /// Invalid drop function in vtable.
230 InvalidDropFn(FnSig<'tcx>),
231 /// Reading a C string that does not end within its allocation.
232 UnterminatedCString(Pointer),
233 /// Dereferencing a dangling pointer after it got freed.
234 PointerUseAfterFree(AllocId),
235 /// Used a pointer outside the bounds it is valid for.
238 msg: CheckInAllocMsg,
239 allocation_size: Size,
241 /// Using an integer as a pointer in the wrong way.
242 DanglingIntPointer(u64, CheckInAllocMsg),
243 /// Used a pointer with bad alignment.
244 AlignmentCheckFailed {
248 /// Writing to read-only memory.
249 WriteToReadOnly(AllocId),
250 // Trying to access the data behind a function pointer.
251 DerefFunctionPointer(AllocId),
252 /// The value validity check found a problem.
253 /// Should only be thrown by `validity.rs` and always point out which part of the value
255 ValidationFailure(String),
256 /// Using a non-boolean `u8` as bool.
258 /// Using a non-character `u32` as character.
260 /// The tag of an enum does not encode an actual discriminant.
262 /// Using a pointer-not-to-a-function as function pointer.
263 InvalidFunctionPointer(Pointer),
264 /// Using a string that is not valid UTF-8,
265 InvalidStr(std::str::Utf8Error),
266 /// Using uninitialized data where it is not allowed.
267 InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
268 /// Working with a local that is not currently live.
270 /// Data size is not equal to target size.
277 impl fmt::Display for UndefinedBehaviorInfo<'_> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 use UndefinedBehaviorInfo::*;
281 Ub(msg) => write!(f, "{}", msg),
282 Unreachable => write!(f, "entering unreachable code"),
283 BoundsCheckFailed { ref len, ref index } => {
284 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
286 DivisionByZero => write!(f, "dividing by zero"),
287 RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
288 PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
289 InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
290 InvalidDropFn(sig) => write!(
292 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
295 UnterminatedCString(p) => write!(
297 "reading a null-terminated string starting at {} with no null found before end of allocation",
300 PointerUseAfterFree(a) => {
301 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
303 PointerOutOfBounds { ptr, msg, allocation_size } => write!(
305 "{}pointer must be in-bounds at offset {}, \
306 but is outside bounds of {} which has size {}",
310 allocation_size.bytes()
312 DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
313 write!(f, "null pointer is not a valid pointer for this operation")
315 DanglingIntPointer(i, msg) => {
316 write!(f, "{}0x{:x} is not a valid pointer", msg, i)
318 AlignmentCheckFailed { required, has } => write!(
320 "accessing memory with alignment {}, but alignment {} is required",
324 WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
325 DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
326 ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
328 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
331 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
333 InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
334 InvalidFunctionPointer(p) => {
335 write!(f, "using {} as function pointer but it does not point to a function", p)
337 InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
338 InvalidUninitBytes(Some((alloc, access))) => write!(
340 "reading {} byte{} of memory starting at {}, \
341 but {} byte{} {} uninitialized starting at {}, \
342 and this operation requires initialized memory",
343 access.access_size.bytes(),
344 pluralize!(access.access_size.bytes()),
345 Pointer::new(*alloc, access.access_offset),
346 access.uninit_size.bytes(),
347 pluralize!(access.uninit_size.bytes()),
348 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
349 Pointer::new(*alloc, access.uninit_offset),
351 InvalidUninitBytes(None) => write!(
353 "using uninitialized data, but this operation requires initialized memory"
355 DeadLocal => write!(f, "accessing a dead local variable"),
356 ScalarSizeMismatch { target_size, data_size } => write!(
358 "scalar size mismatch: expected {} bytes but got {} bytes instead",
359 target_size, data_size
365 /// Error information for when the program did something that might (or might not) be correct
366 /// to do according to the Rust spec, but due to limitations in the interpreter, the
367 /// operation could not be carried out. These limitations can differ between CTFE and the
368 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
369 pub enum UnsupportedOpInfo {
370 /// Free-form case. Only for errors that are never caught!
372 /// Could not find MIR for a function.
374 /// Encountered a pointer where we needed raw bytes.
377 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
379 /// Encountered raw bytes where we needed a pointer.
381 /// Accessing thread local statics
382 ThreadLocalStatic(DefId),
383 /// Accessing an unsupported extern static.
384 ReadExternStatic(DefId),
387 impl fmt::Display for UnsupportedOpInfo {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 use UnsupportedOpInfo::*;
391 Unsupported(ref msg) => write!(f, "{}", msg),
392 ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
393 NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
394 ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
395 ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
396 ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
401 /// Error information for when the program exhausted the resources granted to it
402 /// by the interpreter.
403 pub enum ResourceExhaustionInfo {
404 /// The stack grew too big.
405 StackFrameLimitReached,
406 /// The program ran for too long.
408 /// The exact limit is set by the `const_eval_limit` attribute.
412 impl fmt::Display for ResourceExhaustionInfo {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 use ResourceExhaustionInfo::*;
416 StackFrameLimitReached => {
417 write!(f, "reached the configured maximum number of stack frames")
419 StepLimitReached => {
420 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
426 /// A trait to work around not having trait object upcasting.
427 pub trait AsAny: Any {
428 fn as_any(&self) -> &dyn Any;
430 impl<T: Any> AsAny for T {
432 fn as_any(&self) -> &dyn Any {
437 /// A trait for machine-specific errors (or other "machine stop" conditions).
438 pub trait MachineStopType: AsAny + fmt::Display + Send {
439 /// If `true`, emit a hard error instead of going through the `CONST_ERR` lint
440 fn is_hard_err(&self) -> bool {
445 impl dyn MachineStopType {
447 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
448 self.as_any().downcast_ref()
452 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
453 static_assert_size!(InterpError<'_>, 64);
455 pub enum InterpError<'tcx> {
456 /// The program caused undefined behavior.
457 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
458 /// The program did something the interpreter does not support (some of these *might* be UB
459 /// but the interpreter is not sure).
460 Unsupported(UnsupportedOpInfo),
461 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
462 InvalidProgram(InvalidProgramInfo<'tcx>),
463 /// The program exhausted the interpreter's resources (stack/heap too big,
464 /// execution takes too long, ...).
465 ResourceExhaustion(ResourceExhaustionInfo),
466 /// Stop execution for a machine-controlled reason. This is never raised by
467 /// the core engine itself.
468 MachineStop(Box<dyn MachineStopType>),
471 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
473 impl fmt::Display for InterpError<'_> {
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 Unsupported(ref msg) => write!(f, "{}", msg),
478 InvalidProgram(ref msg) => write!(f, "{}", msg),
479 UndefinedBehavior(ref msg) => write!(f, "{}", msg),
480 ResourceExhaustion(ref msg) => write!(f, "{}", msg),
481 MachineStop(ref msg) => write!(f, "{}", msg),
486 // Forward `Debug` to `Display`, so it does not look awful.
487 impl fmt::Debug for InterpError<'_> {
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 fmt::Display::fmt(self, f)
493 impl InterpError<'_> {
494 /// Some errors to string formatting even if the error is never printed.
495 /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
496 /// so this method lets us detect them and `bug!` on unexpected errors.
497 pub fn formatted_string(&self) -> bool {
499 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
500 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
501 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) => true,
506 /// Should this error be reported as a hard error, preventing compilation, or a soft error,
507 /// causing a deny-by-default lint?
508 pub fn is_hard_err(&self) -> bool {
511 MachineStop(ref err) => err.is_hard_err(),