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