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