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