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