| use std::any::Any; |
| use std::backtrace::Backtrace; |
| use std::borrow::Cow; |
| use std::fmt; |
| |
| use either::Either; |
| use rustc_ast_ir::Mutability; |
| use rustc_data_structures::sync::Lock; |
| use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; |
| use rustc_macros::{HashStable, TyDecodable, TyEncodable}; |
| use rustc_session::CtfeBacktrace; |
| use rustc_span::def_id::DefId; |
| use rustc_span::{Span, Symbol, DUMMY_SP}; |
| use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; |
| |
| use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; |
| use crate::error; |
| use crate::mir::{ConstAlloc, ConstValue}; |
| use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree}; |
| |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] |
| pub enum ErrorHandled { |
| /// Already reported an error for this evaluation, and the compilation is |
| /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. |
| Reported(ReportedErrorInfo, Span), |
| /// Don't emit an error, the evaluation failed because the MIR was generic |
| /// and the args didn't fully monomorphize it. |
| TooGeneric(Span), |
| } |
| |
| impl From<ErrorGuaranteed> for ErrorHandled { |
| #[inline] |
| fn from(error: ErrorGuaranteed) -> ErrorHandled { |
| ErrorHandled::Reported(error.into(), DUMMY_SP) |
| } |
| } |
| |
| impl ErrorHandled { |
| pub fn with_span(self, span: Span) -> Self { |
| match self { |
| ErrorHandled::Reported(err, _span) => ErrorHandled::Reported(err, span), |
| ErrorHandled::TooGeneric(_span) => ErrorHandled::TooGeneric(span), |
| } |
| } |
| |
| pub fn emit_note(&self, tcx: TyCtxt<'_>) { |
| match self { |
| &ErrorHandled::Reported(err, span) => { |
| if !err.is_tainted_by_errors && !span.is_dummy() { |
| tcx.dcx().emit_note(error::ErroneousConstant { span }); |
| } |
| } |
| &ErrorHandled::TooGeneric(_) => {} |
| } |
| } |
| } |
| |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] |
| pub struct ReportedErrorInfo { |
| error: ErrorGuaranteed, |
| is_tainted_by_errors: bool, |
| } |
| |
| impl ReportedErrorInfo { |
| #[inline] |
| pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo { |
| ReportedErrorInfo { is_tainted_by_errors: true, error } |
| } |
| pub fn is_tainted_by_errors(&self) -> bool { |
| self.is_tainted_by_errors |
| } |
| } |
| |
| impl From<ErrorGuaranteed> for ReportedErrorInfo { |
| #[inline] |
| fn from(error: ErrorGuaranteed) -> ReportedErrorInfo { |
| ReportedErrorInfo { is_tainted_by_errors: false, error } |
| } |
| } |
| |
| impl Into<ErrorGuaranteed> for ReportedErrorInfo { |
| #[inline] |
| fn into(self) -> ErrorGuaranteed { |
| self.error |
| } |
| } |
| |
| TrivialTypeTraversalImpls! { ErrorHandled } |
| |
| pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; |
| pub type EvalStaticInitializerRawResult<'tcx> = Result<ConstAllocation<'tcx>, ErrorHandled>; |
| pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; |
| /// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed |
| /// because the value containts something of type `ty` that is not valtree-compatible. |
| /// The caller can then show an appropriate error; the query does not have the |
| /// necssary context to give good user-facing errors for this case. |
| pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>; |
| |
| #[cfg(target_pointer_width = "64")] |
| rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); |
| |
| /// Packages the kind of error we got from the const code interpreter |
| /// up with a Rust-level backtrace of where the error occurred. |
| /// These should always be constructed by calling `.into()` on |
| /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*` |
| /// macros for this. |
| #[derive(Debug)] |
| pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>); |
| |
| #[derive(Debug)] |
| struct InterpErrorInfoInner<'tcx> { |
| kind: InterpError<'tcx>, |
| backtrace: InterpErrorBacktrace, |
| } |
| |
| #[derive(Debug)] |
| pub struct InterpErrorBacktrace { |
| backtrace: Option<Box<Backtrace>>, |
| } |
| |
| impl InterpErrorBacktrace { |
| pub fn new() -> InterpErrorBacktrace { |
| let capture_backtrace = tls::with_opt(|tcx| { |
| if let Some(tcx) = tcx { |
| *Lock::borrow(&tcx.sess.ctfe_backtrace) |
| } else { |
| CtfeBacktrace::Disabled |
| } |
| }); |
| |
| let backtrace = match capture_backtrace { |
| CtfeBacktrace::Disabled => None, |
| CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), |
| CtfeBacktrace::Immediate => { |
| // Print it now. |
| let backtrace = Backtrace::force_capture(); |
| print_backtrace(&backtrace); |
| None |
| } |
| }; |
| |
| InterpErrorBacktrace { backtrace } |
| } |
| |
| pub fn print_backtrace(&self) { |
| if let Some(backtrace) = self.backtrace.as_ref() { |
| print_backtrace(backtrace); |
| } |
| } |
| } |
| |
| impl<'tcx> InterpErrorInfo<'tcx> { |
| pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { |
| let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; |
| (kind, backtrace) |
| } |
| |
| pub fn into_kind(self) -> InterpError<'tcx> { |
| let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self; |
| kind |
| } |
| |
| #[inline] |
| pub fn kind(&self) -> &InterpError<'tcx> { |
| &self.0.kind |
| } |
| } |
| |
| fn print_backtrace(backtrace: &Backtrace) { |
| eprintln!("\n\nAn error occurred in the MIR interpreter:\n{backtrace}"); |
| } |
| |
| impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { |
| fn from(err: ErrorGuaranteed) -> Self { |
| InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() |
| } |
| } |
| |
| impl From<ErrorHandled> for InterpErrorInfo<'_> { |
| fn from(err: ErrorHandled) -> Self { |
| InterpError::InvalidProgram(match err { |
| ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r), |
| ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric, |
| }) |
| .into() |
| } |
| } |
| |
| impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> { |
| fn from(kind: InterpError<'tcx>) -> Self { |
| InterpErrorInfo(Box::new(InterpErrorInfoInner { |
| kind, |
| backtrace: InterpErrorBacktrace::new(), |
| })) |
| } |
| } |
| |
| /// Error information for when the program we executed turned out not to actually be a valid |
| /// program. This cannot happen in stand-alone Miri (except for layout errors that are only detect |
| /// during monomorphization), but it can happen during CTFE/ConstProp where we work on generic code |
| /// or execution does not have all information available. |
| #[derive(Debug)] |
| pub enum InvalidProgramInfo<'tcx> { |
| /// Resolution can fail if we are in a too generic context. |
| TooGeneric, |
| /// Abort in case errors are already reported. |
| AlreadyReported(ReportedErrorInfo), |
| /// An error occurred during layout computation. |
| Layout(layout::LayoutError<'tcx>), |
| /// An error occurred during FnAbi computation: the passed --target lacks FFI support |
| /// (which unfortunately typeck does not reject). |
| /// Not using `FnAbiError` as that contains a nested `LayoutError`. |
| FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), |
| } |
| |
| /// Details of why a pointer had to be in-bounds. |
| #[derive(Debug, Copy, Clone)] |
| pub enum CheckInAllocMsg { |
| /// We are access memory. |
| MemoryAccessTest, |
| /// We are doing pointer arithmetic. |
| PointerArithmeticTest, |
| /// We are doing pointer offset_from. |
| OffsetFromTest, |
| /// None of the above -- generic/unspecific inbounds test. |
| InboundsTest, |
| } |
| |
| /// Details of which pointer is not aligned. |
| #[derive(Debug, Copy, Clone)] |
| pub enum CheckAlignMsg { |
| /// The accessed pointer did not have proper alignment. |
| AccessedPtr, |
| /// The access ocurred with a place that was based on a misaligned pointer. |
| BasedOn, |
| } |
| |
| #[derive(Debug, Copy, Clone)] |
| pub enum InvalidMetaKind { |
| /// Size of a `[T]` is too big |
| SliceTooBig, |
| /// Size of a DST is too big |
| TooBig, |
| } |
| |
| impl IntoDiagArg for InvalidMetaKind { |
| fn into_diag_arg(self) -> DiagArgValue { |
| DiagArgValue::Str(Cow::Borrowed(match self { |
| InvalidMetaKind::SliceTooBig => "slice_too_big", |
| InvalidMetaKind::TooBig => "too_big", |
| })) |
| } |
| } |
| |
| /// Details of an access to uninitialized bytes / bad pointer bytes where it is not allowed. |
| #[derive(Debug, Clone, Copy)] |
| pub struct BadBytesAccess { |
| /// Range of the original memory access. |
| pub access: AllocRange, |
| /// Range of the bad memory that was encountered. (Might not be maximal.) |
| pub bad: AllocRange, |
| } |
| |
| /// Information about a size mismatch. |
| #[derive(Debug)] |
| pub struct ScalarSizeMismatch { |
| pub target_size: u64, |
| pub data_size: u64, |
| } |
| |
| /// Information about a misaligned pointer. |
| #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] |
| pub struct Misalignment { |
| pub has: Align, |
| pub required: Align, |
| } |
| |
| macro_rules! impl_into_diag_arg_through_debug { |
| ($($ty:ty),*$(,)?) => {$( |
| impl IntoDiagArg for $ty { |
| fn into_diag_arg(self) -> DiagArgValue { |
| DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) |
| } |
| } |
| )*} |
| } |
| |
| // These types have nice `Debug` output so we can just use them in diagnostics. |
| impl_into_diag_arg_through_debug! { |
| AllocId, |
| Pointer<AllocId>, |
| AllocRange, |
| } |
| |
| /// Error information for when the program caused Undefined Behavior. |
| #[derive(Debug)] |
| pub enum UndefinedBehaviorInfo<'tcx> { |
| /// Free-form case. Only for errors that are never caught! Used by miri |
| Ub(String), |
| // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically |
| // dispatched |
| /// A custom (free-form) fluent-translated error, created by `err_ub_custom!`. |
| Custom(crate::error::CustomSubdiagnostic<'tcx>), |
| /// Validation error. |
| ValidationError(ValidationErrorInfo<'tcx>), |
| |
| /// Unreachable code was executed. |
| Unreachable, |
| /// A slice/array index projection went out-of-bounds. |
| BoundsCheckFailed { len: u64, index: u64 }, |
| /// Something was divided by 0 (x / 0). |
| DivisionByZero, |
| /// Something was "remainded" by 0 (x % 0). |
| RemainderByZero, |
| /// Signed division overflowed (INT_MIN / -1). |
| DivisionOverflow, |
| /// Signed remainder overflowed (INT_MIN % -1). |
| RemainderOverflow, |
| /// Overflowing inbounds pointer arithmetic. |
| PointerArithOverflow, |
| /// Overflow in arithmetic that may not overflow. |
| ArithOverflow { intrinsic: Symbol }, |
| /// Shift by too much. |
| ShiftOverflow { intrinsic: Symbol, shift_amount: Either<u128, i128> }, |
| /// Invalid metadata in a wide pointer |
| InvalidMeta(InvalidMetaKind), |
| /// Reading a C string that does not end within its allocation. |
| UnterminatedCString(Pointer<AllocId>), |
| /// Using a pointer after it got freed. |
| PointerUseAfterFree(AllocId, CheckInAllocMsg), |
| /// Used a pointer outside the bounds it is valid for. |
| PointerOutOfBounds { |
| alloc_id: AllocId, |
| alloc_size: Size, |
| ptr_offset: i64, |
| /// The size of the memory range that was expected to be in-bounds. |
| inbounds_size: i64, |
| msg: CheckInAllocMsg, |
| }, |
| /// Using an integer as a pointer in the wrong way. |
| DanglingIntPointer { |
| addr: u64, |
| /// The size of the memory range that was expected to be in-bounds (or 0 if we need an |
| /// allocation but not any actual memory there, e.g. for function pointers). |
| inbounds_size: i64, |
| msg: CheckInAllocMsg, |
| }, |
| /// Used a pointer with bad alignment. |
| AlignmentCheckFailed(Misalignment, CheckAlignMsg), |
| /// Writing to read-only memory. |
| WriteToReadOnly(AllocId), |
| /// Trying to access the data behind a function pointer. |
| DerefFunctionPointer(AllocId), |
| /// Trying to access the data behind a vtable pointer. |
| DerefVTablePointer(AllocId), |
| /// Using a non-boolean `u8` as bool. |
| InvalidBool(u8), |
| /// Using a non-character `u32` as character. |
| InvalidChar(u32), |
| /// The tag of an enum does not encode an actual discriminant. |
| InvalidTag(Scalar<AllocId>), |
| /// Using a pointer-not-to-a-function as function pointer. |
| InvalidFunctionPointer(Pointer<AllocId>), |
| /// Using a pointer-not-to-a-vtable as vtable pointer. |
| InvalidVTablePointer(Pointer<AllocId>), |
| /// Using a vtable for the wrong trait. |
| InvalidVTableTrait { |
| expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>, |
| }, |
| /// Using a string that is not valid UTF-8, |
| InvalidStr(std::str::Utf8Error), |
| /// Using uninitialized data where it is not allowed. |
| InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>), |
| /// Working with a local that is not currently live. |
| DeadLocal, |
| /// Data size is not equal to target size. |
| ScalarSizeMismatch(ScalarSizeMismatch), |
| /// A discriminant of an uninhabited enum variant is written. |
| UninhabitedEnumVariantWritten(VariantIdx), |
| /// An uninhabited enum variant is projected. |
| UninhabitedEnumVariantRead(VariantIdx), |
| /// Trying to set discriminant to the niched variant, but the value does not match. |
| InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> }, |
| /// ABI-incompatible argument types. |
| AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, |
| /// ABI-incompatible return types. |
| AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, |
| } |
| |
| #[derive(Debug, Clone, Copy)] |
| pub enum PointerKind { |
| Ref(Mutability), |
| Box, |
| } |
| |
| impl IntoDiagArg for PointerKind { |
| fn into_diag_arg(self) -> DiagArgValue { |
| DiagArgValue::Str( |
| match self { |
| Self::Ref(_) => "ref", |
| Self::Box => "box", |
| } |
| .into(), |
| ) |
| } |
| } |
| |
| #[derive(Debug)] |
| pub struct ValidationErrorInfo<'tcx> { |
| pub path: Option<String>, |
| pub kind: ValidationErrorKind<'tcx>, |
| } |
| |
| #[derive(Debug)] |
| pub enum ExpectedKind { |
| Reference, |
| Box, |
| RawPtr, |
| InitScalar, |
| Bool, |
| Char, |
| Float, |
| Int, |
| FnPtr, |
| EnumTag, |
| Str, |
| } |
| |
| impl From<PointerKind> for ExpectedKind { |
| fn from(x: PointerKind) -> ExpectedKind { |
| match x { |
| PointerKind::Box => ExpectedKind::Box, |
| PointerKind::Ref(_) => ExpectedKind::Reference, |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| pub enum ValidationErrorKind<'tcx> { |
| PointerAsInt { |
| expected: ExpectedKind, |
| }, |
| PartialPointer, |
| PtrToUninhabited { |
| ptr_kind: PointerKind, |
| ty: Ty<'tcx>, |
| }, |
| ConstRefToMutable, |
| ConstRefToExtern, |
| MutableRefToImmutable, |
| UnsafeCellInImmutable, |
| NullFnPtr, |
| NeverVal, |
| NullablePtrOutOfRange { |
| range: WrappingRange, |
| max_value: u128, |
| }, |
| PtrOutOfRange { |
| range: WrappingRange, |
| max_value: u128, |
| }, |
| OutOfRange { |
| value: String, |
| range: WrappingRange, |
| max_value: u128, |
| }, |
| UninhabitedVal { |
| ty: Ty<'tcx>, |
| }, |
| InvalidEnumTag { |
| value: String, |
| }, |
| UninhabitedEnumVariant, |
| Uninit { |
| expected: ExpectedKind, |
| }, |
| InvalidVTablePtr { |
| value: String, |
| }, |
| InvalidMetaWrongTrait { |
| expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>, |
| }, |
| InvalidMetaSliceTooLarge { |
| ptr_kind: PointerKind, |
| }, |
| InvalidMetaTooLarge { |
| ptr_kind: PointerKind, |
| }, |
| UnalignedPtr { |
| ptr_kind: PointerKind, |
| required_bytes: u64, |
| found_bytes: u64, |
| }, |
| NullPtr { |
| ptr_kind: PointerKind, |
| }, |
| DanglingPtrNoProvenance { |
| ptr_kind: PointerKind, |
| pointer: String, |
| }, |
| DanglingPtrOutOfBounds { |
| ptr_kind: PointerKind, |
| }, |
| DanglingPtrUseAfterFree { |
| ptr_kind: PointerKind, |
| }, |
| InvalidBool { |
| value: String, |
| }, |
| InvalidChar { |
| value: String, |
| }, |
| InvalidFnPtr { |
| value: String, |
| }, |
| } |
| |
| /// Error information for when the program did something that might (or might not) be correct |
| /// to do according to the Rust spec, but due to limitations in the interpreter, the |
| /// operation could not be carried out. These limitations can differ between CTFE and the |
| /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. |
| #[derive(Debug)] |
| pub enum UnsupportedOpInfo { |
| /// Free-form case. Only for errors that are never caught! Used by Miri. |
| // FIXME still use translatable diagnostics |
| Unsupported(String), |
| /// Unsized local variables. |
| UnsizedLocal, |
| /// Extern type field with an indeterminate offset. |
| ExternTypeField, |
| // |
| // The variants below are only reachable from CTFE/const prop, miri will never emit them. |
| // |
| /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state |
| /// cannot be represented by the CTFE interpreter. |
| OverwritePartialPointer(Pointer<AllocId>), |
| /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute |
| /// addresses, the resulting state cannot be represented by the CTFE interpreter. |
| ReadPartialPointer(Pointer<AllocId>), |
| /// Encountered a pointer where we needed an integer. |
| ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>), |
| /// Accessing thread local statics |
| ThreadLocalStatic(DefId), |
| /// Accessing an unsupported extern static. |
| ExternStatic(DefId), |
| } |
| |
| /// Error information for when the program exhausted the resources granted to it |
| /// by the interpreter. |
| #[derive(Debug)] |
| pub enum ResourceExhaustionInfo { |
| /// The stack grew too big. |
| StackFrameLimitReached, |
| /// There is not enough memory (on the host) to perform an allocation. |
| MemoryExhausted, |
| /// The address space (of the target) is full. |
| AddressSpaceFull, |
| /// The compiler got an interrupt signal (a user ran out of patience). |
| Interrupted, |
| } |
| |
| /// A trait for machine-specific errors (or other "machine stop" conditions). |
| pub trait MachineStopType: Any + fmt::Debug + Send { |
| /// The diagnostic message for this error |
| fn diagnostic_message(&self) -> DiagMessage; |
| /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to |
| /// fluent for formatting the translated diagnostic message. |
| fn add_args(self: Box<Self>, adder: &mut dyn FnMut(DiagArgName, DiagArgValue)); |
| } |
| |
| impl dyn MachineStopType { |
| #[inline(always)] |
| pub fn downcast_ref<T: Any>(&self) -> Option<&T> { |
| let x: &dyn Any = self; |
| x.downcast_ref() |
| } |
| } |
| |
| #[derive(Debug)] |
| pub enum InterpError<'tcx> { |
| /// The program caused undefined behavior. |
| UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), |
| /// The program did something the interpreter does not support (some of these *might* be UB |
| /// but the interpreter is not sure). |
| Unsupported(UnsupportedOpInfo), |
| /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). |
| InvalidProgram(InvalidProgramInfo<'tcx>), |
| /// The program exhausted the interpreter's resources (stack/heap too big, |
| /// execution takes too long, ...). |
| ResourceExhaustion(ResourceExhaustionInfo), |
| /// Stop execution for a machine-controlled reason. This is never raised by |
| /// the core engine itself. |
| MachineStop(Box<dyn MachineStopType>), |
| } |
| |
| pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>; |
| |
| impl InterpError<'_> { |
| /// Some errors do string formatting even if the error is never printed. |
| /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, |
| /// so this method lets us detect them and `bug!` on unexpected errors. |
| pub fn formatted_string(&self) -> bool { |
| matches!( |
| self, |
| InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) |
| | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) |
| | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) |
| ) |
| } |
| } |
| |
| // Macros for constructing / throwing `InterpError` |
| #[macro_export] |
| macro_rules! err_unsup { |
| ($($tt:tt)*) => { |
| $crate::mir::interpret::InterpError::Unsupported( |
| $crate::mir::interpret::UnsupportedOpInfo::$($tt)* |
| ) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_unsup_format { |
| ($($tt:tt)*) => { $crate::err_unsup!(Unsupported(format!($($tt)*))) }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_inval { |
| ($($tt:tt)*) => { |
| $crate::mir::interpret::InterpError::InvalidProgram( |
| $crate::mir::interpret::InvalidProgramInfo::$($tt)* |
| ) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_ub { |
| ($($tt:tt)*) => { |
| $crate::mir::interpret::InterpError::UndefinedBehavior( |
| $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* |
| ) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_ub_format { |
| ($($tt:tt)*) => { $crate::err_ub!(Ub(format!($($tt)*))) }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_ub_custom { |
| ($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{ |
| $( |
| let ($($name,)*) = ($($value,)*); |
| )? |
| $crate::err_ub!(Custom( |
| $crate::error::CustomSubdiagnostic { |
| msg: || $msg, |
| add_args: Box::new(move |mut set_arg| { |
| $($( |
| set_arg(stringify!($name).into(), rustc_errors::IntoDiagArg::into_diag_arg($name)); |
| )*)? |
| }) |
| } |
| )) |
| }}; |
| } |
| |
| #[macro_export] |
| macro_rules! err_exhaust { |
| ($($tt:tt)*) => { |
| $crate::mir::interpret::InterpError::ResourceExhaustion( |
| $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* |
| ) |
| }; |
| } |
| |
| #[macro_export] |
| macro_rules! err_machine_stop { |
| ($($tt:tt)*) => { |
| $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) |
| }; |
| } |
| |
| // In the `throw_*` macros, avoid `return` to make them work with `try {}`. |
| #[macro_export] |
| macro_rules! throw_unsup { |
| ($($tt:tt)*) => { do yeet $crate::err_unsup!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_unsup_format { |
| ($($tt:tt)*) => { do yeet $crate::err_unsup_format!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_inval { |
| ($($tt:tt)*) => { do yeet $crate::err_inval!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_ub { |
| ($($tt:tt)*) => { do yeet $crate::err_ub!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_ub_format { |
| ($($tt:tt)*) => { do yeet $crate::err_ub_format!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_ub_custom { |
| ($($tt:tt)*) => { do yeet $crate::err_ub_custom!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_exhaust { |
| ($($tt:tt)*) => { do yeet $crate::err_exhaust!($($tt)*) }; |
| } |
| |
| #[macro_export] |
| macro_rules! throw_machine_stop { |
| ($($tt:tt)*) => { do yeet $crate::err_machine_stop!($($tt)*) }; |
| } |