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