rustc_middle/mir/interpret/
error.rs

1use std::any::Any;
2use std::backtrace::Backtrace;
3use std::borrow::Cow;
4use std::{convert, fmt, mem, ops};
5
6use either::Either;
7use rustc_abi::{Align, Size, VariantIdx, WrappingRange};
8use rustc_data_structures::sync::Lock;
9use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg};
10use rustc_macros::{HashStable, TyDecodable, TyEncodable};
11use rustc_session::CtfeBacktrace;
12use rustc_span::def_id::DefId;
13use rustc_span::{DUMMY_SP, Span, Symbol};
14
15use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar};
16use crate::error;
17use crate::mir::{ConstAlloc, ConstValue};
18use crate::ty::{self, Mutability, Ty, TyCtxt, ValTree, layout, tls};
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
21pub enum ErrorHandled {
22    /// Already reported an error for this evaluation, and the compilation is
23    /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
24    Reported(ReportedErrorInfo, Span),
25    /// Don't emit an error, the evaluation failed because the MIR was generic
26    /// and the args didn't fully monomorphize it.
27    TooGeneric(Span),
28}
29
30impl From<ReportedErrorInfo> for ErrorHandled {
31    #[inline]
32    fn from(error: ReportedErrorInfo) -> ErrorHandled {
33        ErrorHandled::Reported(error, DUMMY_SP)
34    }
35}
36
37impl ErrorHandled {
38    pub(crate) fn with_span(self, span: Span) -> Self {
39        match self {
40            ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span),
41            ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span),
42        }
43    }
44
45    pub fn emit_note(&self, tcx: TyCtxt<'_>) {
46        match self {
47            &ErrorHandled::Reported(err, span) => {
48                if !err.allowed_in_infallible && !span.is_dummy() {
49                    tcx.dcx().emit_note(error::ErroneousConstant { span });
50                }
51            }
52            &ErrorHandled::TooGeneric(_) => {}
53        }
54    }
55}
56
57#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
58pub struct ReportedErrorInfo {
59    error: ErrorGuaranteed,
60    /// Whether this error is allowed to show up even in otherwise "infallible" promoteds.
61    /// This is for things like overflows during size computation or resource exhaustion.
62    allowed_in_infallible: bool,
63}
64
65impl ReportedErrorInfo {
66    #[inline]
67    pub fn const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo {
68        ReportedErrorInfo { allowed_in_infallible: false, error }
69    }
70
71    /// Use this when the error that led to this is *not* a const-eval error
72    /// (e.g., a layout or type checking error).
73    #[inline]
74    pub fn non_const_eval_error(error: ErrorGuaranteed) -> ReportedErrorInfo {
75        ReportedErrorInfo { allowed_in_infallible: true, error }
76    }
77
78    /// Use this when the error that led to this *is* a const-eval error, but
79    /// we do allow it to occur in infallible constants (e.g., resource exhaustion).
80    #[inline]
81    pub fn allowed_in_infallible(error: ErrorGuaranteed) -> ReportedErrorInfo {
82        ReportedErrorInfo { allowed_in_infallible: true, error }
83    }
84
85    pub fn is_allowed_in_infallible(&self) -> bool {
86        self.allowed_in_infallible
87    }
88}
89
90impl From<ReportedErrorInfo> for ErrorGuaranteed {
91    #[inline]
92    fn from(val: ReportedErrorInfo) -> Self {
93        val.error
94    }
95}
96
97/// An error type for the `const_to_valtree` query. Some error should be reported with a "use-site span",
98/// which means the query cannot emit the error, so those errors are represented as dedicated variants here.
99#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
100pub enum ValTreeCreationError<'tcx> {
101    /// The constant is too big to be valtree'd.
102    NodesOverflow,
103    /// The constant references mutable or external memory, so it cannot be valtree'd.
104    InvalidConst,
105    /// Values of this type, or this particular value, are not supported as valtrees.
106    NonSupportedType(Ty<'tcx>),
107    /// The error has already been handled by const evaluation.
108    ErrorHandled(ErrorHandled),
109}
110
111impl<'tcx> From<ErrorHandled> for ValTreeCreationError<'tcx> {
112    fn from(err: ErrorHandled) -> Self {
113        ValTreeCreationError::ErrorHandled(err)
114    }
115}
116
117impl<'tcx> From<InterpErrorInfo<'tcx>> for ValTreeCreationError<'tcx> {
118    fn from(err: InterpErrorInfo<'tcx>) -> Self {
119        // An error occurred outside the const-eval query, as part of constructing the valtree. We
120        // don't currently preserve the details of this error, since `InterpErrorInfo` cannot be put
121        // into a query result and it can only be access of some mutable or external memory.
122        let (_kind, backtrace) = err.into_parts();
123        backtrace.print_backtrace();
124        ValTreeCreationError::InvalidConst
125    }
126}
127
128impl<'tcx> ValTreeCreationError<'tcx> {
129    pub(crate) fn with_span(self, span: Span) -> Self {
130        use ValTreeCreationError::*;
131        match self {
132            ErrorHandled(handled) => ErrorHandled(handled.with_span(span)),
133            other => other,
134        }
135    }
136}
137
138pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
139pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>;
140pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
141pub type EvalToValTreeResult<'tcx> = Result<ValTree<'tcx>, ValTreeCreationError<'tcx>>;
142
143#[cfg(target_pointer_width = "64")]
144rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
145
146/// Packages the kind of error we got from the const code interpreter
147/// up with a Rust-level backtrace of where the error occurred.
148/// These should always be constructed by calling `.into()` on
149/// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
150/// macros for this.
151///
152/// Interpreter errors must *not* be silently discarded (that will lead to a panic). Instead,
153/// explicitly call `discard_err` if this is really the right thing to do. Note that if
154/// this happens during const-eval or in Miri, it could lead to a UB error being lost!
155#[derive(Debug)]
156pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
157
158#[derive(Debug)]
159struct InterpErrorInfoInner<'tcx> {
160    kind: InterpErrorKind<'tcx>,
161    backtrace: InterpErrorBacktrace,
162}
163
164#[derive(Debug)]
165pub struct InterpErrorBacktrace {
166    backtrace: Option<Box<Backtrace>>,
167}
168
169impl InterpErrorBacktrace {
170    pub fn new() -> InterpErrorBacktrace {
171        let capture_backtrace = tls::with_opt(|tcx| {
172            if let Some(tcx) = tcx {
173                *Lock::borrow(&tcx.sess.ctfe_backtrace)
174            } else {
175                CtfeBacktrace::Disabled
176            }
177        });
178
179        let backtrace = match capture_backtrace {
180            CtfeBacktrace::Disabled => None,
181            CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
182            CtfeBacktrace::Immediate => {
183                // Print it now.
184                let backtrace = Backtrace::force_capture();
185                print_backtrace(&backtrace);
186                None
187            }
188        };
189
190        InterpErrorBacktrace { backtrace }
191    }
192
193    pub fn print_backtrace(&self) {
194        if let Some(backtrace) = self.backtrace.as_ref() {
195            print_backtrace(backtrace);
196        }
197    }
198}
199
200impl<'tcx> InterpErrorInfo<'tcx> {
201    pub fn into_parts(self) -> (InterpErrorKind<'tcx>, InterpErrorBacktrace) {
202        let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
203        (kind, backtrace)
204    }
205
206    pub fn into_kind(self) -> InterpErrorKind<'tcx> {
207        self.0.kind
208    }
209
210    pub fn from_parts(kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
211        Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
212    }
213
214    #[inline]
215    pub fn kind(&self) -> &InterpErrorKind<'tcx> {
216        &self.0.kind
217    }
218}
219
220fn print_backtrace(backtrace: &Backtrace) {
221    eprintln!("\n\nAn error occurred in the MIR interpreter:\n{backtrace}");
222}
223
224impl From<ErrorHandled> for InterpErrorInfo<'_> {
225    fn from(err: ErrorHandled) -> Self {
226        InterpErrorKind::InvalidProgram(match err {
227            ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r),
228            ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric,
229        })
230        .into()
231    }
232}
233
234impl<'tcx> From<InterpErrorKind<'tcx>> for InterpErrorInfo<'tcx> {
235    fn from(kind: InterpErrorKind<'tcx>) -> Self {
236        InterpErrorInfo(Box::new(InterpErrorInfoInner {
237            kind,
238            backtrace: InterpErrorBacktrace::new(),
239        }))
240    }
241}
242
243/// Error information for when the program we executed turned out not to actually be a valid
244/// program. This cannot happen in stand-alone Miri (except for layout errors that are only detect
245/// during monomorphization), but it can happen during CTFE/ConstProp where we work on generic code
246/// or execution does not have all information available.
247#[derive(Debug)]
248pub enum InvalidProgramInfo<'tcx> {
249    /// Resolution can fail if we are in a too generic context.
250    TooGeneric,
251    /// Abort in case errors are already reported.
252    AlreadyReported(ReportedErrorInfo),
253    /// An error occurred during layout computation.
254    Layout(layout::LayoutError<'tcx>),
255}
256
257/// Details of why a pointer had to be in-bounds.
258#[derive(Debug, Copy, Clone)]
259pub enum CheckInAllocMsg {
260    /// We are accessing memory.
261    MemoryAccess,
262    /// We are doing pointer arithmetic.
263    InboundsPointerArithmetic,
264    /// None of the above -- generic/unspecific inbounds test.
265    Dereferenceable,
266}
267
268/// Details of which pointer is not aligned.
269#[derive(Debug, Copy, Clone)]
270pub enum CheckAlignMsg {
271    /// The accessed pointer did not have proper alignment.
272    AccessedPtr,
273    /// The access occurred with a place that was based on a misaligned pointer.
274    BasedOn,
275}
276
277#[derive(Debug, Copy, Clone)]
278pub enum InvalidMetaKind {
279    /// Size of a `[T]` is too big
280    SliceTooBig,
281    /// Size of a DST is too big
282    TooBig,
283}
284
285impl IntoDiagArg for InvalidMetaKind {
286    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
287        DiagArgValue::Str(Cow::Borrowed(match self {
288            InvalidMetaKind::SliceTooBig => "slice_too_big",
289            InvalidMetaKind::TooBig => "too_big",
290        }))
291    }
292}
293
294/// Details of an access to uninitialized bytes / bad pointer bytes where it is not allowed.
295#[derive(Debug, Clone, Copy)]
296pub struct BadBytesAccess {
297    /// Range of the original memory access.
298    pub access: AllocRange,
299    /// Range of the bad memory that was encountered. (Might not be maximal.)
300    pub bad: AllocRange,
301}
302
303/// Information about a size mismatch.
304#[derive(Debug)]
305pub struct ScalarSizeMismatch {
306    pub target_size: u64,
307    pub data_size: u64,
308}
309
310/// Information about a misaligned pointer.
311#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
312pub struct Misalignment {
313    pub has: Align,
314    pub required: Align,
315}
316
317macro_rules! impl_into_diag_arg_through_debug {
318    ($($ty:ty),*$(,)?) => {$(
319        impl IntoDiagArg for $ty {
320            fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
321                DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
322            }
323        }
324    )*}
325}
326
327// These types have nice `Debug` output so we can just use them in diagnostics.
328impl_into_diag_arg_through_debug! {
329    AllocId,
330    Pointer<AllocId>,
331    AllocRange,
332}
333
334/// Error information for when the program caused Undefined Behavior.
335#[derive(Debug)]
336pub enum UndefinedBehaviorInfo<'tcx> {
337    /// Free-form case. Only for errors that are never caught! Used by miri
338    Ub(String),
339    // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
340    // dispatched
341    /// A custom (free-form) fluent-translated error, created by `err_ub_custom!`.
342    Custom(crate::error::CustomSubdiagnostic<'tcx>),
343    /// Validation error.
344    ValidationError(ValidationErrorInfo<'tcx>),
345
346    /// Unreachable code was executed.
347    Unreachable,
348    /// A slice/array index projection went out-of-bounds.
349    BoundsCheckFailed { len: u64, index: u64 },
350    /// Something was divided by 0 (x / 0).
351    DivisionByZero,
352    /// Something was "remainded" by 0 (x % 0).
353    RemainderByZero,
354    /// Signed division overflowed (INT_MIN / -1).
355    DivisionOverflow,
356    /// Signed remainder overflowed (INT_MIN % -1).
357    RemainderOverflow,
358    /// Overflowing inbounds pointer arithmetic.
359    PointerArithOverflow,
360    /// Overflow in arithmetic that may not overflow.
361    ArithOverflow { intrinsic: Symbol },
362    /// Shift by too much.
363    ShiftOverflow { intrinsic: Symbol, shift_amount: Either<u128, i128> },
364    /// Invalid metadata in a wide pointer
365    InvalidMeta(InvalidMetaKind),
366    /// Reading a C string that does not end within its allocation.
367    UnterminatedCString(Pointer<AllocId>),
368    /// Using a pointer after it got freed.
369    PointerUseAfterFree(AllocId, CheckInAllocMsg),
370    /// Used a pointer outside the bounds it is valid for.
371    PointerOutOfBounds {
372        alloc_id: AllocId,
373        alloc_size: Size,
374        ptr_offset: i64,
375        /// The size of the memory range that was expected to be in-bounds.
376        inbounds_size: i64,
377        msg: CheckInAllocMsg,
378    },
379    /// Using an integer as a pointer in the wrong way.
380    DanglingIntPointer {
381        addr: u64,
382        /// The size of the memory range that was expected to be in-bounds (or 0 if we need an
383        /// allocation but not any actual memory there, e.g. for function pointers).
384        inbounds_size: i64,
385        msg: CheckInAllocMsg,
386    },
387    /// Used a pointer with bad alignment.
388    AlignmentCheckFailed(Misalignment, CheckAlignMsg),
389    /// Writing to read-only memory.
390    WriteToReadOnly(AllocId),
391    /// Trying to access the data behind a function pointer.
392    DerefFunctionPointer(AllocId),
393    /// Trying to access the data behind a vtable pointer.
394    DerefVTablePointer(AllocId),
395    /// Trying to access the actual type id.
396    DerefTypeIdPointer(AllocId),
397    /// Using a non-boolean `u8` as bool.
398    InvalidBool(u8),
399    /// Using a non-character `u32` as character.
400    InvalidChar(u32),
401    /// The tag of an enum does not encode an actual discriminant.
402    InvalidTag(Scalar<AllocId>),
403    /// Using a pointer-not-to-a-function as function pointer.
404    InvalidFunctionPointer(Pointer<AllocId>),
405    /// Using a pointer-not-to-a-vtable as vtable pointer.
406    InvalidVTablePointer(Pointer<AllocId>),
407    /// Using a vtable for the wrong trait.
408    InvalidVTableTrait {
409        /// The vtable that was actually referenced by the wide pointer metadata.
410        vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
411        /// The vtable that was expected at the point in MIR that it was accessed.
412        expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
413    },
414    /// Using a string that is not valid UTF-8,
415    InvalidStr(std::str::Utf8Error),
416    /// Using uninitialized data where it is not allowed.
417    InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>),
418    /// Working with a local that is not currently live.
419    DeadLocal,
420    /// Data size is not equal to target size.
421    ScalarSizeMismatch(ScalarSizeMismatch),
422    /// A discriminant of an uninhabited enum variant is written.
423    UninhabitedEnumVariantWritten(VariantIdx),
424    /// An uninhabited enum variant is projected.
425    UninhabitedEnumVariantRead(Option<VariantIdx>),
426    /// Trying to set discriminant to the niched variant, but the value does not match.
427    InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
428    /// ABI-incompatible argument types.
429    AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
430    /// ABI-incompatible return types.
431    AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
432}
433
434#[derive(Debug, Clone, Copy)]
435pub enum PointerKind {
436    Ref(Mutability),
437    Box,
438}
439
440impl IntoDiagArg for PointerKind {
441    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
442        DiagArgValue::Str(
443            match self {
444                Self::Ref(_) => "ref",
445                Self::Box => "box",
446            }
447            .into(),
448        )
449    }
450}
451
452#[derive(Debug)]
453pub struct ValidationErrorInfo<'tcx> {
454    pub path: Option<String>,
455    pub kind: ValidationErrorKind<'tcx>,
456}
457
458#[derive(Debug)]
459pub enum ExpectedKind {
460    Reference,
461    Box,
462    RawPtr,
463    InitScalar,
464    Bool,
465    Char,
466    Float,
467    Int,
468    FnPtr,
469    EnumTag,
470    Str,
471}
472
473impl From<PointerKind> for ExpectedKind {
474    fn from(x: PointerKind) -> ExpectedKind {
475        match x {
476            PointerKind::Box => ExpectedKind::Box,
477            PointerKind::Ref(_) => ExpectedKind::Reference,
478        }
479    }
480}
481
482#[derive(Debug)]
483pub enum ValidationErrorKind<'tcx> {
484    PointerAsInt {
485        expected: ExpectedKind,
486    },
487    PartialPointer,
488    PtrToUninhabited {
489        ptr_kind: PointerKind,
490        ty: Ty<'tcx>,
491    },
492    MutableRefToImmutable,
493    UnsafeCellInImmutable,
494    MutableRefInConst,
495    NullFnPtr,
496    NeverVal,
497    NullablePtrOutOfRange {
498        range: WrappingRange,
499        max_value: u128,
500    },
501    PtrOutOfRange {
502        range: WrappingRange,
503        max_value: u128,
504    },
505    OutOfRange {
506        value: String,
507        range: WrappingRange,
508        max_value: u128,
509    },
510    UninhabitedVal {
511        ty: Ty<'tcx>,
512    },
513    InvalidEnumTag {
514        value: String,
515    },
516    UninhabitedEnumVariant,
517    Uninit {
518        expected: ExpectedKind,
519    },
520    InvalidVTablePtr {
521        value: String,
522    },
523    InvalidMetaWrongTrait {
524        /// The vtable that was actually referenced by the wide pointer metadata.
525        vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
526        /// The vtable that was expected at the point in MIR that it was accessed.
527        expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
528    },
529    InvalidMetaSliceTooLarge {
530        ptr_kind: PointerKind,
531    },
532    InvalidMetaTooLarge {
533        ptr_kind: PointerKind,
534    },
535    UnalignedPtr {
536        ptr_kind: PointerKind,
537        required_bytes: u64,
538        found_bytes: u64,
539    },
540    NullPtr {
541        ptr_kind: PointerKind,
542    },
543    DanglingPtrNoProvenance {
544        ptr_kind: PointerKind,
545        pointer: String,
546    },
547    DanglingPtrOutOfBounds {
548        ptr_kind: PointerKind,
549    },
550    DanglingPtrUseAfterFree {
551        ptr_kind: PointerKind,
552    },
553    InvalidBool {
554        value: String,
555    },
556    InvalidChar {
557        value: String,
558    },
559    InvalidFnPtr {
560        value: String,
561    },
562}
563
564/// Error information for when the program did something that might (or might not) be correct
565/// to do according to the Rust spec, but due to limitations in the interpreter, the
566/// operation could not be carried out. These limitations can differ between CTFE and the
567/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
568#[derive(Debug)]
569pub enum UnsupportedOpInfo {
570    /// Free-form case. Only for errors that are never caught! Used by Miri.
571    // FIXME still use translatable diagnostics
572    Unsupported(String),
573    /// Unsized local variables.
574    UnsizedLocal,
575    /// Extern type field with an indeterminate offset.
576    ExternTypeField,
577    //
578    // The variants below are only reachable from CTFE/const prop, miri will never emit them.
579    //
580    /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
581    /// cannot be represented by the CTFE interpreter.
582    OverwritePartialPointer(Pointer<AllocId>),
583    /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute
584    /// addresses, the resulting state cannot be represented by the CTFE interpreter.
585    ReadPartialPointer(Pointer<AllocId>),
586    /// Encountered a pointer where we needed an integer.
587    ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>),
588    /// Accessing thread local statics
589    ThreadLocalStatic(DefId),
590    /// Accessing an unsupported extern static.
591    ExternStatic(DefId),
592}
593
594/// Error information for when the program exhausted the resources granted to it
595/// by the interpreter.
596#[derive(Debug)]
597pub enum ResourceExhaustionInfo {
598    /// The stack grew too big.
599    StackFrameLimitReached,
600    /// There is not enough memory (on the host) to perform an allocation.
601    MemoryExhausted,
602    /// The address space (of the target) is full.
603    AddressSpaceFull,
604    /// The compiler got an interrupt signal (a user ran out of patience).
605    Interrupted,
606}
607
608/// A trait for machine-specific errors (or other "machine stop" conditions).
609pub trait MachineStopType: Any + fmt::Debug + Send {
610    /// The diagnostic message for this error
611    fn diagnostic_message(&self) -> DiagMessage;
612    /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
613    /// fluent for formatting the translated diagnostic message.
614    fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue));
615}
616
617impl dyn MachineStopType {
618    #[inline(always)]
619    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
620        let x: &dyn Any = self;
621        x.downcast_ref()
622    }
623}
624
625#[derive(Debug)]
626pub enum InterpErrorKind<'tcx> {
627    /// The program caused undefined behavior.
628    UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
629    /// The program did something the interpreter does not support (some of these *might* be UB
630    /// but the interpreter is not sure).
631    Unsupported(UnsupportedOpInfo),
632    /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
633    InvalidProgram(InvalidProgramInfo<'tcx>),
634    /// The program exhausted the interpreter's resources (stack/heap too big,
635    /// execution takes too long, ...).
636    ResourceExhaustion(ResourceExhaustionInfo),
637    /// Stop execution for a machine-controlled reason. This is never raised by
638    /// the core engine itself.
639    MachineStop(Box<dyn MachineStopType>),
640}
641
642impl InterpErrorKind<'_> {
643    /// Some errors do string formatting even if the error is never printed.
644    /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
645    /// so this method lets us detect them and `bug!` on unexpected errors.
646    pub fn formatted_string(&self) -> bool {
647        matches!(
648            self,
649            InterpErrorKind::Unsupported(UnsupportedOpInfo::Unsupported(_))
650                | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. })
651                | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
652        )
653    }
654}
655
656// Macros for constructing / throwing `InterpErrorKind`
657#[macro_export]
658macro_rules! err_unsup {
659    ($($tt:tt)*) => {
660        $crate::mir::interpret::InterpErrorKind::Unsupported(
661            $crate::mir::interpret::UnsupportedOpInfo::$($tt)*
662        )
663    };
664}
665
666#[macro_export]
667macro_rules! err_unsup_format {
668    ($($tt:tt)*) => { $crate::err_unsup!(Unsupported(format!($($tt)*))) };
669}
670
671#[macro_export]
672macro_rules! err_inval {
673    ($($tt:tt)*) => {
674        $crate::mir::interpret::InterpErrorKind::InvalidProgram(
675            $crate::mir::interpret::InvalidProgramInfo::$($tt)*
676        )
677    };
678}
679
680#[macro_export]
681macro_rules! err_ub {
682    ($($tt:tt)*) => {
683        $crate::mir::interpret::InterpErrorKind::UndefinedBehavior(
684            $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)*
685        )
686    };
687}
688
689#[macro_export]
690macro_rules! err_ub_format {
691    ($($tt:tt)*) => { $crate::err_ub!(Ub(format!($($tt)*))) };
692}
693
694#[macro_export]
695macro_rules! err_ub_custom {
696    ($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{
697        $(
698            let ($($name,)*) = ($($value,)*);
699        )?
700        $crate::err_ub!(Custom(
701            $crate::error::CustomSubdiagnostic {
702                msg: || $msg,
703                add_args: Box::new(move |mut set_arg| {
704                    $($(
705                        set_arg(stringify!($name).into(), rustc_errors::IntoDiagArg::into_diag_arg($name, &mut None));
706                    )*)?
707                })
708            }
709        ))
710    }};
711}
712
713#[macro_export]
714macro_rules! err_exhaust {
715    ($($tt:tt)*) => {
716        $crate::mir::interpret::InterpErrorKind::ResourceExhaustion(
717            $crate::mir::interpret::ResourceExhaustionInfo::$($tt)*
718        )
719    };
720}
721
722#[macro_export]
723macro_rules! err_machine_stop {
724    ($($tt:tt)*) => {
725        $crate::mir::interpret::InterpErrorKind::MachineStop(Box::new($($tt)*))
726    };
727}
728
729// In the `throw_*` macros, avoid `return` to make them work with `try {}`.
730#[macro_export]
731macro_rules! throw_unsup {
732    ($($tt:tt)*) => { do yeet $crate::err_unsup!($($tt)*) };
733}
734
735#[macro_export]
736macro_rules! throw_unsup_format {
737    ($($tt:tt)*) => { do yeet $crate::err_unsup_format!($($tt)*) };
738}
739
740#[macro_export]
741macro_rules! throw_inval {
742    ($($tt:tt)*) => { do yeet $crate::err_inval!($($tt)*) };
743}
744
745#[macro_export]
746macro_rules! throw_ub {
747    ($($tt:tt)*) => { do yeet $crate::err_ub!($($tt)*) };
748}
749
750#[macro_export]
751macro_rules! throw_ub_format {
752    ($($tt:tt)*) => { do yeet $crate::err_ub_format!($($tt)*) };
753}
754
755#[macro_export]
756macro_rules! throw_ub_custom {
757    ($($tt:tt)*) => { do yeet $crate::err_ub_custom!($($tt)*) };
758}
759
760#[macro_export]
761macro_rules! throw_exhaust {
762    ($($tt:tt)*) => { do yeet $crate::err_exhaust!($($tt)*) };
763}
764
765#[macro_export]
766macro_rules! throw_machine_stop {
767    ($($tt:tt)*) => { do yeet $crate::err_machine_stop!($($tt)*) };
768}
769
770/// Guard type that panics on drop.
771#[derive(Debug)]
772struct Guard;
773
774impl Drop for Guard {
775    fn drop(&mut self) {
776        // We silence the guard if we are already panicking, to avoid double-panics.
777        if !std::thread::panicking() {
778            panic!(
779                "an interpreter error got improperly discarded; use `discard_err()` if this is intentional"
780            );
781        }
782    }
783}
784
785/// The result type used by the interpreter. This is a newtype around `Result`
786/// to block access to operations like `ok()` that discard UB errors.
787///
788/// We also make things panic if this type is ever implicitly dropped.
789#[derive(Debug)]
790#[must_use]
791pub struct InterpResult_<'tcx, T> {
792    res: Result<T, InterpErrorInfo<'tcx>>,
793    guard: Guard,
794}
795
796// Type alias to be able to set a default type argument.
797pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>;
798
799impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> {
800    type Output = T;
801    type Residual = InterpResult_<'tcx, convert::Infallible>;
802
803    #[inline]
804    fn from_output(output: Self::Output) -> Self {
805        InterpResult_::new(Ok(output))
806    }
807
808    #[inline]
809    fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> {
810        match self.disarm() {
811            Ok(v) => ops::ControlFlow::Continue(v),
812            Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))),
813        }
814    }
815}
816
817impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> {
818    #[inline]
819    #[track_caller]
820    fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self {
821        match residual.disarm() {
822            Err(e) => Self::new(Err(e)),
823        }
824    }
825}
826
827// Allow `yeet`ing `InterpError` in functions returning `InterpResult_`.
828impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult_<'tcx, T> {
829    #[inline]
830    fn from_residual(ops::Yeet(e): ops::Yeet<InterpErrorKind<'tcx>>) -> Self {
831        Self::new(Err(e.into()))
832    }
833}
834
835// Allow `?` on `Result<_, InterpError>` in functions returning `InterpResult_`.
836// This is useful e.g. for `option.ok_or_else(|| err_ub!(...))`.
837impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> ops::FromResidual<Result<convert::Infallible, E>>
838    for InterpResult_<'tcx, T>
839{
840    #[inline]
841    fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
842        match residual {
843            Err(e) => Self::new(Err(e.into())),
844        }
845    }
846}
847
848impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> From<Result<T, E>> for InterpResult<'tcx, T> {
849    #[inline]
850    fn from(value: Result<T, E>) -> Self {
851        Self::new(value.map_err(|e| e.into()))
852    }
853}
854
855impl<'tcx, T, V: FromIterator<T>> FromIterator<InterpResult<'tcx, T>> for InterpResult<'tcx, V> {
856    fn from_iter<I: IntoIterator<Item = InterpResult<'tcx, T>>>(iter: I) -> Self {
857        Self::new(iter.into_iter().map(|x| x.disarm()).collect())
858    }
859}
860
861impl<'tcx, T> InterpResult_<'tcx, T> {
862    #[inline(always)]
863    fn new(res: Result<T, InterpErrorInfo<'tcx>>) -> Self {
864        Self { res, guard: Guard }
865    }
866
867    #[inline(always)]
868    fn disarm(self) -> Result<T, InterpErrorInfo<'tcx>> {
869        mem::forget(self.guard);
870        self.res
871    }
872
873    /// Discard the error information in this result. Only use this if ignoring Undefined Behavior is okay!
874    #[inline]
875    pub fn discard_err(self) -> Option<T> {
876        self.disarm().ok()
877    }
878
879    /// Look at the `Result` wrapped inside of this.
880    /// Must only be used to report the error!
881    #[inline]
882    pub fn report_err(self) -> Result<T, InterpErrorInfo<'tcx>> {
883        self.disarm()
884    }
885
886    #[inline]
887    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> {
888        InterpResult_::new(self.disarm().map(f))
889    }
890
891    #[inline]
892    pub fn map_err_info(
893        self,
894        f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>,
895    ) -> InterpResult<'tcx, T> {
896        InterpResult_::new(self.disarm().map_err(f))
897    }
898
899    #[inline]
900    pub fn map_err_kind(
901        self,
902        f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>,
903    ) -> InterpResult<'tcx, T> {
904        InterpResult_::new(self.disarm().map_err(|mut e| {
905            e.0.kind = f(e.0.kind);
906            e
907        }))
908    }
909
910    #[inline]
911    pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> {
912        InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind)))
913    }
914
915    #[inline]
916    #[track_caller]
917    pub fn unwrap(self) -> T {
918        self.disarm().unwrap()
919    }
920
921    #[inline]
922    #[track_caller]
923    pub fn unwrap_or_else(self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> T) -> T {
924        self.disarm().unwrap_or_else(f)
925    }
926
927    #[inline]
928    #[track_caller]
929    pub fn expect(self, msg: &str) -> T {
930        self.disarm().expect(msg)
931    }
932
933    #[inline]
934    pub fn and_then<U>(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> {
935        InterpResult_::new(self.disarm().and_then(|t| f(t).disarm()))
936    }
937
938    /// Returns success if both `self` and `other` succeed, while ensuring we don't
939    /// accidentally drop an error.
940    ///
941    /// If both are an error, `self` will be reported.
942    #[inline]
943    pub fn and<U>(self, other: InterpResult<'tcx, U>) -> InterpResult<'tcx, (T, U)> {
944        match self.disarm() {
945            Ok(t) => interp_ok((t, other?)),
946            Err(e) => {
947                // Discard the other error.
948                drop(other.disarm());
949                // Return `self`.
950                InterpResult_::new(Err(e))
951            }
952        }
953    }
954}
955
956#[inline(always)]
957pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> {
958    InterpResult_::new(Ok(x))
959}