]> git.lizzy.rs Git - rust.git/blob - src/librustc/mir/interpret/error.rs
Rollup merge of #69520 - kornelski:e69492, r=cramertj
[rust.git] / src / librustc / mir / interpret / error.rs
1 use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef};
2
3 use crate::hir::map::definitions::DefPathData;
4 use crate::mir;
5 use crate::mir::interpret::ConstValue;
6 use crate::ty::layout::{Align, LayoutError, Size};
7 use crate::ty::query::TyCtxtAt;
8 use crate::ty::tls;
9 use crate::ty::{self, layout, Ty};
10
11 use backtrace::Backtrace;
12 use rustc_data_structures::sync::Lock;
13 use rustc_errors::{struct_span_err, DiagnosticBuilder};
14 use rustc_hir as hir;
15 use rustc_macros::HashStable;
16 use rustc_session::CtfeBacktrace;
17 use rustc_span::{Pos, Span};
18 use rustc_target::spec::abi::Abi;
19 use std::{any::Any, fmt};
20
21 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)]
22 pub enum ErrorHandled {
23     /// Already reported a lint or an error for this evaluation.
24     Reported,
25     /// Don't emit an error, the evaluation failed because the MIR was generic
26     /// and the substs didn't fully monomorphize it.
27     TooGeneric,
28 }
29
30 impl ErrorHandled {
31     pub fn assert_reported(self) {
32         match self {
33             ErrorHandled::Reported => {}
34             ErrorHandled::TooGeneric => bug!(
35                 "MIR interpretation failed without reporting an error \
36                  even though it was fully monomorphized"
37             ),
38         }
39     }
40 }
41
42 CloneTypeFoldableImpls! {
43     ErrorHandled,
44 }
45
46 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
47 pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
48
49 #[derive(Debug)]
50 pub struct ConstEvalErr<'tcx> {
51     pub span: Span,
52     pub error: crate::mir::interpret::InterpError<'tcx>,
53     pub stacktrace: Vec<FrameInfo<'tcx>>,
54 }
55
56 #[derive(Debug)]
57 pub struct FrameInfo<'tcx> {
58     /// This span is in the caller.
59     pub call_site: Span,
60     pub instance: ty::Instance<'tcx>,
61     pub lint_root: Option<hir::HirId>,
62 }
63
64 impl<'tcx> fmt::Display for FrameInfo<'tcx> {
65     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66         ty::tls::with(|tcx| {
67             if tcx.def_key(self.instance.def_id()).disambiguated_data.data
68                 == DefPathData::ClosureExpr
69             {
70                 write!(f, "inside call to closure")?;
71             } else {
72                 write!(f, "inside call to `{}`", self.instance)?;
73             }
74             if !self.call_site.is_dummy() {
75                 let lo = tcx.sess.source_map().lookup_char_pos(self.call_site.lo());
76                 write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
77             }
78             Ok(())
79         })
80     }
81 }
82
83 impl<'tcx> ConstEvalErr<'tcx> {
84     pub fn struct_error(
85         &self,
86         tcx: TyCtxtAt<'tcx>,
87         message: &str,
88         emit: impl FnOnce(DiagnosticBuilder<'_>),
89     ) -> Result<(), ErrorHandled> {
90         self.struct_generic(tcx, message, emit, None)
91     }
92
93     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
94         match self.struct_error(tcx, message, |mut e| e.emit()) {
95             Ok(_) => ErrorHandled::Reported,
96             Err(x) => x,
97         }
98     }
99
100     pub fn report_as_lint(
101         &self,
102         tcx: TyCtxtAt<'tcx>,
103         message: &str,
104         lint_root: hir::HirId,
105         span: Option<Span>,
106     ) -> ErrorHandled {
107         match self.struct_generic(
108             tcx,
109             message,
110             |mut lint: DiagnosticBuilder<'_>| {
111                 // Apply the span.
112                 if let Some(span) = span {
113                     let primary_spans = lint.span.primary_spans().to_vec();
114                     // point at the actual error as the primary span
115                     lint.replace_span_with(span);
116                     // point to the `const` statement as a secondary span
117                     // they don't have any label
118                     for sp in primary_spans {
119                         if sp != span {
120                             lint.span_label(sp, "");
121                         }
122                     }
123                 }
124                 lint.emit();
125             },
126             Some(lint_root),
127         ) {
128             Ok(_) => ErrorHandled::Reported,
129             Err(err) => err,
130         }
131     }
132
133     /// Create a diagnostic for this const eval error.
134     ///
135     /// Sets the message passed in via `message` and adds span labels with detailed error
136     /// information before handing control back to `emit` to do any final processing.
137     /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
138     /// function to dispose of the diagnostic properly.
139     ///
140     /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
141     /// (Except that for some errors, we ignore all that -- see `must_error` below.)
142     fn struct_generic(
143         &self,
144         tcx: TyCtxtAt<'tcx>,
145         message: &str,
146         emit: impl FnOnce(DiagnosticBuilder<'_>),
147         lint_root: Option<hir::HirId>,
148     ) -> Result<(), ErrorHandled> {
149         let must_error = match self.error {
150             err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
151                 return Err(ErrorHandled::TooGeneric);
152             }
153             err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
154             // We must *always* hard error on these, even if the caller wants just a lint.
155             err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
156             _ => false,
157         };
158         trace!("reporting const eval failure at {:?}", self.span);
159
160         let err_msg = match &self.error {
161             InterpError::MachineStop(msg) => {
162                 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
163                 // Should be turned into a string by now.
164                 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
165             }
166             err => err.to_string(),
167         };
168
169         let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
170             if let Some(span_msg) = span_msg {
171                 err.span_label(self.span, span_msg);
172             }
173             // Add spans for the stacktrace.
174             // Skip the last, which is just the environment of the constant.  The stacktrace
175             // is sometimes empty because we create "fake" eval contexts in CTFE to do work
176             // on constant values.
177             if !self.stacktrace.is_empty() {
178                 for frame_info in &self.stacktrace[..self.stacktrace.len() - 1] {
179                     err.span_label(frame_info.call_site, frame_info.to_string());
180                 }
181             }
182             // Let the caller finish the job.
183             emit(err)
184         };
185
186         if must_error {
187             // The `message` makes little sense here, this is a more serious error than the
188             // caller thinks anyway.
189             // See <https://github.com/rust-lang/rust/pull/63152>.
190             finish(struct_error(tcx, &err_msg), None);
191         } else {
192             // Regular case.
193             if let Some(lint_root) = lint_root {
194                 // Report as lint.
195                 let hir_id = self
196                     .stacktrace
197                     .iter()
198                     .rev()
199                     .filter_map(|frame| frame.lint_root)
200                     .next()
201                     .unwrap_or(lint_root);
202                 tcx.struct_span_lint_hir(
203                     rustc_session::lint::builtin::CONST_ERR,
204                     hir_id,
205                     tcx.span,
206                     |lint| finish(lint.build(message), Some(err_msg)),
207                 );
208             } else {
209                 // Report as hard error.
210                 finish(struct_error(tcx, message), Some(err_msg));
211             }
212         }
213         Ok(())
214     }
215 }
216
217 pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
218     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
219 }
220
221 /// Packages the kind of error we got from the const code interpreter
222 /// up with a Rust-level backtrace of where the error occurred.
223 /// Thsese should always be constructed by calling `.into()` on
224 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
225 /// macros for this.
226 #[derive(Debug)]
227 pub struct InterpErrorInfo<'tcx> {
228     pub kind: InterpError<'tcx>,
229     backtrace: Option<Box<Backtrace>>,
230 }
231
232 impl fmt::Display for InterpErrorInfo<'_> {
233     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234         write!(f, "{}", self.kind)
235     }
236 }
237
238 impl InterpErrorInfo<'_> {
239     pub fn print_backtrace(&mut self) {
240         if let Some(ref mut backtrace) = self.backtrace {
241             print_backtrace(&mut *backtrace);
242         }
243     }
244 }
245
246 fn print_backtrace(backtrace: &mut Backtrace) {
247     backtrace.resolve();
248     eprintln!("\n\nAn error occurred in miri:\n{:?}", backtrace);
249 }
250
251 impl From<ErrorHandled> for InterpErrorInfo<'_> {
252     fn from(err: ErrorHandled) -> Self {
253         match err {
254             ErrorHandled::Reported => err_inval!(ReferencedConstant),
255             ErrorHandled::TooGeneric => err_inval!(TooGeneric),
256         }
257         .into()
258     }
259 }
260
261 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
262     fn from(kind: InterpError<'tcx>) -> Self {
263         let capture_backtrace = tls::with_context_opt(|ctxt| {
264             if let Some(ctxt) = ctxt {
265                 *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace)
266             } else {
267                 CtfeBacktrace::Disabled
268             }
269         });
270
271         let backtrace = match capture_backtrace {
272             CtfeBacktrace::Disabled => None,
273             CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())),
274             CtfeBacktrace::Immediate => {
275                 // Print it now.
276                 let mut backtrace = Backtrace::new_unresolved();
277                 print_backtrace(&mut backtrace);
278                 None
279             }
280         };
281
282         InterpErrorInfo { kind, backtrace }
283     }
284 }
285
286 /// Error information for when the program we executed turned out not to actually be a valid
287 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
288 /// where we work on generic code or execution does not have all information available.
289 pub enum InvalidProgramInfo<'tcx> {
290     /// Resolution can fail if we are in a too generic context.
291     TooGeneric,
292     /// Cannot compute this constant because it depends on another one
293     /// which already produced an error.
294     ReferencedConstant,
295     /// Abort in case type errors are reached.
296     TypeckError,
297     /// An error occurred during layout computation.
298     Layout(layout::LayoutError<'tcx>),
299 }
300
301 impl fmt::Debug for InvalidProgramInfo<'_> {
302     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303         use InvalidProgramInfo::*;
304         match self {
305             TooGeneric => write!(f, "encountered overly generic constant"),
306             ReferencedConstant => write!(f, "referenced constant has errors"),
307             TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"),
308             Layout(ref err) => write!(f, "{}", err),
309         }
310     }
311 }
312
313 /// Error information for when the program caused Undefined Behavior.
314 pub enum UndefinedBehaviorInfo {
315     /// Free-form case. Only for errors that are never caught!
316     Ub(String),
317     /// Free-form case for experimental UB. Only for errors that are never caught!
318     UbExperimental(String),
319     /// Unreachable code was executed.
320     Unreachable,
321     /// An enum discriminant was set to a value which was outside the range of valid values.
322     InvalidDiscriminant(ScalarMaybeUndef),
323     /// A slice/array index projection went out-of-bounds.
324     BoundsCheckFailed { len: u64, index: u64 },
325     /// Something was divided by 0 (x / 0).
326     DivisionByZero,
327     /// Something was "remainded" by 0 (x % 0).
328     RemainderByZero,
329     /// Overflowing inbounds pointer arithmetic.
330     PointerArithOverflow,
331     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
332     InvalidMeta(&'static str),
333 }
334
335 impl fmt::Debug for UndefinedBehaviorInfo {
336     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337         use UndefinedBehaviorInfo::*;
338         match self {
339             Ub(msg) | UbExperimental(msg) => write!(f, "{}", msg),
340             Unreachable => write!(f, "entering unreachable code"),
341             InvalidDiscriminant(val) => write!(f, "encountering invalid enum discriminant {}", val),
342             BoundsCheckFailed { ref len, ref index } => write!(
343                 f,
344                 "indexing out of bounds: the len is {:?} but the index is {:?}",
345                 len, index
346             ),
347             DivisionByZero => write!(f, "dividing by zero"),
348             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
349             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
350             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
351         }
352     }
353 }
354
355 /// Error information for when the program did something that might (or might not) be correct
356 /// to do according to the Rust spec, but due to limitations in the interpreter, the
357 /// operation could not be carried out. These limitations can differ between CTFE and the
358 /// Miri engine, e.g., CTFE does not support casting pointers to "real" integers.
359 ///
360 /// Currently, we also use this as fall-back error kind for errors that have not been
361 /// categorized yet.
362 pub enum UnsupportedOpInfo<'tcx> {
363     /// Free-form case. Only for errors that are never caught!
364     Unsupported(String),
365
366     /// When const-prop encounters a situation it does not support, it raises this error.
367     /// This must not allocate for performance reasons (hence `str`, not `String`).
368     ConstPropUnsupported(&'static str),
369
370     // -- Everything below is not categorized yet --
371     FunctionAbiMismatch(Abi, Abi),
372     FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
373     FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>),
374     FunctionArgCountMismatch,
375     UnterminatedCString(Pointer),
376     DanglingPointerDeref,
377     DoubleFree,
378     InvalidMemoryAccess,
379     InvalidFunctionPointer,
380     InvalidBool,
381     PointerOutOfBounds {
382         ptr: Pointer,
383         msg: CheckInAllocMsg,
384         allocation_size: Size,
385     },
386     InvalidNullPointerUsage,
387     ReadPointerAsBytes,
388     ReadBytesAsPointer,
389     ReadForeignStatic,
390     InvalidPointerMath,
391     ReadUndefBytes(Size),
392     DeadLocal,
393     InvalidBoolOp(mir::BinOp),
394     UnimplementedTraitSelection,
395     CalledClosureAsFunction,
396     NoMirFor(String),
397     DerefFunctionPointer,
398     ExecuteMemory,
399     InvalidChar(u128),
400     OutOfTls,
401     TlsOutOfBounds,
402     AlignmentCheckFailed {
403         required: Align,
404         has: Align,
405     },
406     ValidationFailure(String),
407     VtableForArgumentlessMethod,
408     ModifiedConstantMemory,
409     ModifiedStatic,
410     TypeNotPrimitive(Ty<'tcx>),
411     ReallocatedWrongMemoryKind(String, String),
412     DeallocatedWrongMemoryKind(String, String),
413     ReallocateNonBasePtr,
414     DeallocateNonBasePtr,
415     IncorrectAllocationInformation(Size, Size, Align, Align),
416     HeapAllocZeroBytes,
417     HeapAllocNonPowerOfTwoAlignment(u64),
418     ReadFromReturnPointer,
419     PathNotFound(Vec<String>),
420     TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
421 }
422
423 impl fmt::Debug for UnsupportedOpInfo<'tcx> {
424     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425         use UnsupportedOpInfo::*;
426         match self {
427             PointerOutOfBounds { ptr, msg, allocation_size } => write!(
428                 f,
429                 "{} failed: pointer must be in-bounds at offset {}, \
430                            but is outside bounds of allocation {} which has size {}",
431                 msg,
432                 ptr.offset.bytes(),
433                 ptr.alloc_id,
434                 allocation_size.bytes()
435             ),
436             ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
437             NoMirFor(ref func) => write!(f, "no MIR for `{}`", func),
438             FunctionAbiMismatch(caller_abi, callee_abi) => write!(
439                 f,
440                 "tried to call a function with ABI {:?} using caller ABI {:?}",
441                 callee_abi, caller_abi
442             ),
443             FunctionArgMismatch(caller_ty, callee_ty) => write!(
444                 f,
445                 "tried to call a function with argument of type {:?} \
446                            passing data of type {:?}",
447                 callee_ty, caller_ty
448             ),
449             TransmuteSizeDiff(from_ty, to_ty) => write!(
450                 f,
451                 "tried to transmute from {:?} to {:?}, but their sizes differed",
452                 from_ty, to_ty
453             ),
454             FunctionRetMismatch(caller_ty, callee_ty) => write!(
455                 f,
456                 "tried to call a function with return type {:?} \
457                            passing return place of type {:?}",
458                 callee_ty, caller_ty
459             ),
460             FunctionArgCountMismatch => {
461                 write!(f, "tried to call a function with incorrect number of arguments")
462             }
463             ReallocatedWrongMemoryKind(ref old, ref new) => {
464                 write!(f, "tried to reallocate memory from `{}` to `{}`", old, new)
465             }
466             DeallocatedWrongMemoryKind(ref old, ref new) => {
467                 write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new)
468             }
469             InvalidChar(c) => {
470                 write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c)
471             }
472             AlignmentCheckFailed { required, has } => write!(
473                 f,
474                 "tried to access memory with alignment {}, but alignment {} is required",
475                 has.bytes(),
476                 required.bytes()
477             ),
478             TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty),
479             PathNotFound(ref path) => write!(f, "cannot find path {:?}", path),
480             IncorrectAllocationInformation(size, size2, align, align2) => write!(
481                 f,
482                 "incorrect alloc info: expected size {} and align {}, \
483                            got size {} and align {}",
484                 size.bytes(),
485                 align.bytes(),
486                 size2.bytes(),
487                 align2.bytes()
488             ),
489             InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"),
490             DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"),
491             DoubleFree => write!(f, "tried to deallocate dangling pointer"),
492             InvalidFunctionPointer => {
493                 write!(f, "tried to use a function pointer after offsetting it")
494             }
495             InvalidBool => write!(f, "invalid boolean value read"),
496             InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"),
497             ReadPointerAsBytes => write!(
498                 f,
499                 "a raw memory access tried to access part of a pointer value as raw \
500                     bytes"
501             ),
502             ReadBytesAsPointer => {
503                 write!(f, "a memory access tried to interpret some bytes as a pointer")
504             }
505             ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"),
506             InvalidPointerMath => write!(
507                 f,
508                 "attempted to do invalid arithmetic on pointers that would leak base \
509                     addresses, e.g., comparing pointers into different allocations"
510             ),
511             DeadLocal => write!(f, "tried to access a dead local variable"),
512             DerefFunctionPointer => write!(f, "tried to dereference a function pointer"),
513             ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"),
514             OutOfTls => write!(f, "reached the maximum number of representable TLS keys"),
515             TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"),
516             CalledClosureAsFunction => {
517                 write!(f, "tried to call a closure through a function pointer")
518             }
519             VtableForArgumentlessMethod => {
520                 write!(f, "tried to call a vtable function without arguments")
521             }
522             ModifiedConstantMemory => write!(f, "tried to modify constant memory"),
523             ModifiedStatic => write!(
524                 f,
525                 "tried to modify a static's initial value from another static's \
526                     initializer"
527             ),
528             ReallocateNonBasePtr => write!(
529                 f,
530                 "tried to reallocate with a pointer not to the beginning of an \
531                     existing object"
532             ),
533             DeallocateNonBasePtr => write!(
534                 f,
535                 "tried to deallocate with a pointer not to the beginning of an \
536                     existing object"
537             ),
538             HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"),
539             ReadFromReturnPointer => write!(f, "tried to read from the return pointer"),
540             UnimplementedTraitSelection => {
541                 write!(f, "there were unresolved type arguments during trait selection")
542             }
543             InvalidBoolOp(_) => write!(f, "invalid boolean operation"),
544             UnterminatedCString(_) => write!(
545                 f,
546                 "attempted to get length of a null-terminated string, but no null \
547                     found before end of allocation"
548             ),
549             ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"),
550             HeapAllocNonPowerOfTwoAlignment(_) => write!(
551                 f,
552                 "tried to re-, de-, or allocate heap memory with alignment that is \
553                     not a power of two"
554             ),
555             Unsupported(ref msg) => write!(f, "{}", msg),
556             ConstPropUnsupported(ref msg) => {
557                 write!(f, "Constant propagation encountered an unsupported situation: {}", msg)
558             }
559         }
560     }
561 }
562
563 /// Error information for when the program exhausted the resources granted to it
564 /// by the interpreter.
565 pub enum ResourceExhaustionInfo {
566     /// The stack grew too big.
567     StackFrameLimitReached,
568     /// The program ran into an infinite loop.
569     InfiniteLoop,
570 }
571
572 impl fmt::Debug for ResourceExhaustionInfo {
573     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574         use ResourceExhaustionInfo::*;
575         match self {
576             StackFrameLimitReached => {
577                 write!(f, "reached the configured maximum number of stack frames")
578             }
579             InfiniteLoop => write!(
580                 f,
581                 "duplicate interpreter state observed here, const evaluation will never \
582                     terminate"
583             ),
584         }
585     }
586 }
587
588 pub enum InterpError<'tcx> {
589     /// The program caused undefined behavior.
590     UndefinedBehavior(UndefinedBehaviorInfo),
591     /// The program did something the interpreter does not support (some of these *might* be UB
592     /// but the interpreter is not sure).
593     Unsupported(UnsupportedOpInfo<'tcx>),
594     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
595     InvalidProgram(InvalidProgramInfo<'tcx>),
596     /// The program exhausted the interpreter's resources (stack/heap too big,
597     /// execution takes too long, ...).
598     ResourceExhaustion(ResourceExhaustionInfo),
599     /// Stop execution for a machine-controlled reason. This is never raised by
600     /// the core engine itself.
601     MachineStop(Box<dyn Any + Send>),
602 }
603
604 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
605
606 impl fmt::Display for InterpError<'_> {
607     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
608         // Forward `Display` to `Debug`.
609         write!(f, "{:?}", self)
610     }
611 }
612
613 impl fmt::Debug for InterpError<'_> {
614     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615         use InterpError::*;
616         match *self {
617             Unsupported(ref msg) => write!(f, "{:?}", msg),
618             InvalidProgram(ref msg) => write!(f, "{:?}", msg),
619             UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
620             ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
621             MachineStop(_) => bug!("unhandled MachineStop"),
622         }
623     }
624 }
625
626 impl InterpError<'_> {
627     /// Some errors allocate to be created as they contain free-form strings.
628     /// And sometimes we want to be sure that did not happen as it is a
629     /// waste of resources.
630     pub fn allocates(&self) -> bool {
631         match self {
632             InterpError::MachineStop(_)
633             | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
634             | InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_))
635             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
636             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true,
637             _ => false,
638         }
639     }
640 }