1 use super::{AllocId, ConstAlloc, Pointer, Scalar};
2 
3 use crate::mir::interpret::ConstValue;
4 use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
5 
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};
13 
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.
20     Linted,
21     /// Don't emit an error, the evaluation failed because the MIR was generic
22     /// and the substs didn't fully monomorphize it.
23     TooGeneric,
24 }
25 
26 impl From<ErrorReported> for ErrorHandled {
from(err: ErrorReported) -> ErrorHandled27     fn from(err: ErrorReported) -> ErrorHandled {
28         ErrorHandled::Reported(err)
29     }
30 }
31 
32 TrivialTypeFoldableAndLiftImpls! {
33     ErrorHandled,
34 }
35 
36 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
37 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
38 
struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx>39 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
40     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
41 }
42 
43 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
44 static_assert_size!(InterpErrorInfo<'_>, 8);
45 
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 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
50 /// macros for this.
51 #[derive(Debug)]
52 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
53 
54 #[derive(Debug)]
55 struct InterpErrorInfoInner<'tcx> {
56     kind: InterpError<'tcx>,
57     backtrace: Option<Box<Backtrace>>,
58 }
59 
60 impl fmt::Display for InterpErrorInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result61     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62         write!(f, "{}", self.0.kind)
63     }
64 }
65 
66 impl InterpErrorInfo<'tcx> {
print_backtrace(&self)67     pub fn print_backtrace(&self) {
68         if let Some(backtrace) = self.0.backtrace.as_ref() {
69             print_backtrace(backtrace);
70         }
71     }
72 
into_kind(self) -> InterpError<'tcx>73     pub fn into_kind(self) -> InterpError<'tcx> {
74         let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
75         kind
76     }
77 
78     #[inline]
kind(&self) -> &InterpError<'tcx>79     pub fn kind(&self) -> &InterpError<'tcx> {
80         &self.0.kind
81     }
82 }
83 
print_backtrace(backtrace: &Backtrace)84 fn print_backtrace(backtrace: &Backtrace) {
85     eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
86 }
87 
88 impl From<ErrorHandled> for InterpErrorInfo<'_> {
from(err: ErrorHandled) -> Self89     fn from(err: ErrorHandled) -> Self {
90         match err {
91             ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
92                 err_inval!(ReferencedConstant)
93             }
94             ErrorHandled::TooGeneric => err_inval!(TooGeneric),
95         }
96         .into()
97     }
98 }
99 
100 impl From<ErrorReported> for InterpErrorInfo<'_> {
from(err: ErrorReported) -> Self101     fn from(err: ErrorReported) -> Self {
102         InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
103     }
104 }
105 
106 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
from(kind: InterpError<'tcx>) -> Self107     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)
111             } else {
112                 CtfeBacktrace::Disabled
113             }
114         });
115 
116         let backtrace = match capture_backtrace {
117             CtfeBacktrace::Disabled => None,
118             CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
119             CtfeBacktrace::Immediate => {
120                 // Print it now.
121                 let backtrace = Backtrace::force_capture();
122                 print_backtrace(&backtrace);
123                 None
124             }
125         };
126 
127         InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
128     }
129 }
130 
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.
136     TooGeneric,
137     /// Cannot compute this constant because it depends on another one
138     /// which already produced an error.
139     ReferencedConstant,
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>),
148 }
149 
150 impl fmt::Display for InvalidProgramInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result151     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152         use InvalidProgramInfo::*;
153         match self {
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")
158             }
159             Layout(ref err) => write!(f, "{}", err),
160             TransmuteSizeDiff(from_ty, to_ty) => write!(
161                 f,
162                 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
163                 from_ty, to_ty
164             ),
165             SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty),
166         }
167     }
168 }
169 
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 dereferencing a pointer (i.e., creating a place).
174     DerefTest,
175     /// We are access memory.
176     MemoryAccessTest,
177     /// We are doing pointer arithmetic.
178     PointerArithmeticTest,
179     /// None of the above -- generic/unspecific inbounds test.
180     InboundsTest,
181 }
182 
183 impl fmt::Display for CheckInAllocMsg {
184     /// When this is printed as an error the context looks like this:
185     /// "{msg}0x01 is not a valid pointer".
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result186     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187         write!(
188             f,
189             "{}",
190             match *self {
191                 CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
192                 CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
193                 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ",
194                 CheckInAllocMsg::InboundsTest => "",
195             }
196         )
197     }
198 }
199 
200 /// Details of an access to uninitialized bytes where it is not allowed.
201 #[derive(Debug)]
202 pub struct UninitBytesAccess {
203     /// Location of the original memory access.
204     pub access_offset: Size,
205     /// Size of the original memory access.
206     pub access_size: Size,
207     /// Location of the first uninitialized byte that was accessed.
208     pub uninit_offset: Size,
209     /// Number of consecutive uninitialized bytes that were accessed.
210     pub uninit_size: Size,
211 }
212 
213 /// Error information for when the program caused Undefined Behavior.
214 pub enum UndefinedBehaviorInfo<'tcx> {
215     /// Free-form case. Only for errors that are never caught!
216     Ub(String),
217     /// Unreachable code was executed.
218     Unreachable,
219     /// A slice/array index projection went out-of-bounds.
220     BoundsCheckFailed {
221         len: u64,
222         index: u64,
223     },
224     /// Something was divided by 0 (x / 0).
225     DivisionByZero,
226     /// Something was "remainded" by 0 (x % 0).
227     RemainderByZero,
228     /// Overflowing inbounds pointer arithmetic.
229     PointerArithOverflow,
230     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
231     InvalidMeta(&'static str),
232     /// Invalid drop function in vtable.
233     InvalidVtableDropFn(FnSig<'tcx>),
234     /// Invalid size in a vtable: too large.
235     InvalidVtableSize,
236     /// Invalid alignment in a vtable: too large, or not a power of 2.
237     InvalidVtableAlignment(String),
238     /// Reading a C string that does not end within its allocation.
239     UnterminatedCString(Pointer),
240     /// Dereferencing a dangling pointer after it got freed.
241     PointerUseAfterFree(AllocId),
242     /// Used a pointer outside the bounds it is valid for.
243     /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
244     PointerOutOfBounds {
245         alloc_id: AllocId,
246         alloc_size: Size,
247         ptr_offset: i64,
248         ptr_size: Size,
249         msg: CheckInAllocMsg,
250     },
251     /// Using an integer as a pointer in the wrong way.
252     DanglingIntPointer(u64, CheckInAllocMsg),
253     /// Used a pointer with bad alignment.
254     AlignmentCheckFailed {
255         required: Align,
256         has: Align,
257     },
258     /// Writing to read-only memory.
259     WriteToReadOnly(AllocId),
260     // Trying to access the data behind a function pointer.
261     DerefFunctionPointer(AllocId),
262     /// The value validity check found a problem.
263     /// Should only be thrown by `validity.rs` and always point out which part of the value
264     /// is the problem.
265     ValidationFailure {
266         /// The "path" to the value in question, e.g. `.0[5].field` for a struct
267         /// field in the 6th element of an array that is the first element of a tuple.
268         path: Option<String>,
269         msg: String,
270     },
271     /// Using a non-boolean `u8` as bool.
272     InvalidBool(u8),
273     /// Using a non-character `u32` as character.
274     InvalidChar(u32),
275     /// The tag of an enum does not encode an actual discriminant.
276     InvalidTag(Scalar),
277     /// Using a pointer-not-to-a-function as function pointer.
278     InvalidFunctionPointer(Pointer),
279     /// Using a string that is not valid UTF-8,
280     InvalidStr(std::str::Utf8Error),
281     /// Using uninitialized data where it is not allowed.
282     InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
283     /// Working with a local that is not currently live.
284     DeadLocal,
285     /// Data size is not equal to target size.
286     ScalarSizeMismatch {
287         target_size: u64,
288         data_size: u64,
289     },
290     /// A discriminant of an uninhabited enum variant is written.
291     UninhabitedEnumVariantWritten,
292 }
293 
294 impl fmt::Display for UndefinedBehaviorInfo<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result295     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296         use UndefinedBehaviorInfo::*;
297         match self {
298             Ub(msg) => write!(f, "{}", msg),
299             Unreachable => write!(f, "entering unreachable code"),
300             BoundsCheckFailed { ref len, ref index } => {
301                 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
302             }
303             DivisionByZero => write!(f, "dividing by zero"),
304             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
305             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
306             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
307             InvalidVtableDropFn(sig) => write!(
308                 f,
309                 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
310                 sig
311             ),
312             InvalidVtableSize => {
313                 write!(f, "invalid vtable: size is bigger than largest supported object")
314             }
315             InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
316             UnterminatedCString(p) => write!(
317                 f,
318                 "reading a null-terminated string starting at {:?} with no null found before end of allocation",
319                 p,
320             ),
321             PointerUseAfterFree(a) => {
322                 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
323             }
324             PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
325                 write!(
326                     f,
327                     "{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
328                     msg,
329                     alloc_id = alloc_id,
330                     alloc_size = alloc_size.bytes(),
331                     ptr_offset = ptr_offset,
332                 )
333             }
334             PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
335                 f,
336                 "{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
337                 msg,
338                 alloc_id = alloc_id,
339                 alloc_size = alloc_size.bytes(),
340                 ptr_size = ptr_size.bytes(),
341                 ptr_size_p = pluralize!(ptr_size.bytes()),
342                 ptr_offset = ptr_offset,
343             ),
344             DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => {
345                 write!(f, "null pointer is not a valid pointer for this operation")
346             }
347             DanglingIntPointer(i, msg) => {
348                 write!(f, "{}0x{:x} is not a valid pointer", msg, i)
349             }
350             AlignmentCheckFailed { required, has } => write!(
351                 f,
352                 "accessing memory with alignment {}, but alignment {} is required",
353                 has.bytes(),
354                 required.bytes()
355             ),
356             WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
357             DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
358             ValidationFailure { path: None, msg } => write!(f, "type validation failed: {}", msg),
359             ValidationFailure { path: Some(path), msg } => {
360                 write!(f, "type validation failed at {}: {}", path, msg)
361             }
362             InvalidBool(b) => {
363                 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
364             }
365             InvalidChar(c) => {
366                 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
367             }
368             InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
369             InvalidFunctionPointer(p) => {
370                 write!(f, "using {:?} as function pointer but it does not point to a function", p)
371             }
372             InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
373             InvalidUninitBytes(Some((alloc, access))) => write!(
374                 f,
375                 "reading {} byte{} of memory starting at {:?}, \
376                  but {} byte{} {} uninitialized starting at {:?}, \
377                  and this operation requires initialized memory",
378                 access.access_size.bytes(),
379                 pluralize!(access.access_size.bytes()),
380                 Pointer::new(*alloc, access.access_offset),
381                 access.uninit_size.bytes(),
382                 pluralize!(access.uninit_size.bytes()),
383                 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
384                 Pointer::new(*alloc, access.uninit_offset),
385             ),
386             InvalidUninitBytes(None) => write!(
387                 f,
388                 "using uninitialized data, but this operation requires initialized memory"
389             ),
390             DeadLocal => write!(f, "accessing a dead local variable"),
391             ScalarSizeMismatch { target_size, data_size } => write!(
392                 f,
393                 "scalar size mismatch: expected {} bytes but got {} bytes instead",
394                 target_size, data_size
395             ),
396             UninhabitedEnumVariantWritten => {
397                 write!(f, "writing discriminant of an uninhabited enum")
398             }
399         }
400     }
401 }
402 
403 /// Error information for when the program did something that might (or might not) be correct
404 /// to do according to the Rust spec, but due to limitations in the interpreter, the
405 /// operation could not be carried out. These limitations can differ between CTFE and the
406 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
407 pub enum UnsupportedOpInfo {
408     /// Free-form case. Only for errors that are never caught!
409     Unsupported(String),
410     /// Encountered a pointer where we needed raw bytes.
411     ReadPointerAsBytes,
412     /// Overwriting parts of a pointer; the resulting state cannot be represented in our
413     /// `Allocation` data structure.
414     PartialPointerOverwrite(Pointer<AllocId>),
415     //
416     // The variants below are only reachable from CTFE/const prop, miri will never emit them.
417     //
418     /// Accessing thread local statics
419     ThreadLocalStatic(DefId),
420     /// Accessing an unsupported extern static.
421     ReadExternStatic(DefId),
422 }
423 
424 impl fmt::Display for UnsupportedOpInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result425     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426         use UnsupportedOpInfo::*;
427         match self {
428             Unsupported(ref msg) => write!(f, "{}", msg),
429             ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
430             PartialPointerOverwrite(ptr) => {
431                 write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr)
432             }
433             ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
434             ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
435         }
436     }
437 }
438 
439 /// Error information for when the program exhausted the resources granted to it
440 /// by the interpreter.
441 pub enum ResourceExhaustionInfo {
442     /// The stack grew too big.
443     StackFrameLimitReached,
444     /// The program ran for too long.
445     ///
446     /// The exact limit is set by the `const_eval_limit` attribute.
447     StepLimitReached,
448     /// There is not enough memory to perform an allocation.
449     MemoryExhausted,
450 }
451 
452 impl fmt::Display for ResourceExhaustionInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result453     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454         use ResourceExhaustionInfo::*;
455         match self {
456             StackFrameLimitReached => {
457                 write!(f, "reached the configured maximum number of stack frames")
458             }
459             StepLimitReached => {
460                 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
461             }
462             MemoryExhausted => {
463                 write!(f, "tried to allocate more memory than available to compiler")
464             }
465         }
466     }
467 }
468 
469 /// A trait to work around not having trait object upcasting.
470 pub trait AsAny: Any {
as_any(&self) -> &dyn Any471     fn as_any(&self) -> &dyn Any;
472 }
473 impl<T: Any> AsAny for T {
474     #[inline(always)]
as_any(&self) -> &dyn Any475     fn as_any(&self) -> &dyn Any {
476         self
477     }
478 }
479 
480 /// A trait for machine-specific errors (or other "machine stop" conditions).
481 pub trait MachineStopType: AsAny + fmt::Display + Send {
482     /// If `true`, emit a hard error instead of going through the `CONST_ERR` lint
is_hard_err(&self) -> bool483     fn is_hard_err(&self) -> bool {
484         false
485     }
486 }
487 
488 impl dyn MachineStopType {
489     #[inline(always)]
downcast_ref<T: Any>(&self) -> Option<&T>490     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
491         self.as_any().downcast_ref()
492     }
493 }
494 
495 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
496 static_assert_size!(InterpError<'_>, 64);
497 
498 pub enum InterpError<'tcx> {
499     /// The program caused undefined behavior.
500     UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
501     /// The program did something the interpreter does not support (some of these *might* be UB
502     /// but the interpreter is not sure).
503     Unsupported(UnsupportedOpInfo),
504     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
505     InvalidProgram(InvalidProgramInfo<'tcx>),
506     /// The program exhausted the interpreter's resources (stack/heap too big,
507     /// execution takes too long, ...).
508     ResourceExhaustion(ResourceExhaustionInfo),
509     /// Stop execution for a machine-controlled reason. This is never raised by
510     /// the core engine itself.
511     MachineStop(Box<dyn MachineStopType>),
512 }
513 
514 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
515 
516 impl fmt::Display for InterpError<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result517     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518         use InterpError::*;
519         match *self {
520             Unsupported(ref msg) => write!(f, "{}", msg),
521             InvalidProgram(ref msg) => write!(f, "{}", msg),
522             UndefinedBehavior(ref msg) => write!(f, "{}", msg),
523             ResourceExhaustion(ref msg) => write!(f, "{}", msg),
524             MachineStop(ref msg) => write!(f, "{}", msg),
525         }
526     }
527 }
528 
529 // Forward `Debug` to `Display`, so it does not look awful.
530 impl fmt::Debug for InterpError<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result531     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532         fmt::Display::fmt(self, f)
533     }
534 }
535 
536 impl InterpError<'_> {
537     /// Some errors do string formatting even if the error is never printed.
538     /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
539     /// so this method lets us detect them and `bug!` on unexpected errors.
formatted_string(&self) -> bool540     pub fn formatted_string(&self) -> bool {
541         matches!(
542             self,
543             InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
544                 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
545                 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
546         )
547     }
548 
549     /// Should this error be reported as a hard error, preventing compilation, or a soft error,
550     /// causing a deny-by-default lint?
is_hard_err(&self) -> bool551     pub fn is_hard_err(&self) -> bool {
552         use InterpError::*;
553         match *self {
554             MachineStop(ref err) => err.is_hard_err(),
555             UndefinedBehavior(_) => true,
556             ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) => true,
557             _ => false,
558         }
559     }
560 }
561