use std::any::Any;
use std::backtrace::Backtrace;
use std::borrow::Cow;
use std::{convert, fmt, mem, ops};
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::{DUMMY_SP, Span, Symbol};
use rustc_target::abi::{Align, Size, VariantIdx, WrappingRange, call};
use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar};
use crate::error;
use crate::mir::{ConstAlloc, ConstValue};
use crate::ty::{self, Ty, TyCtxt, ValTree, layout, tls};
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
pub enum ErrorHandled {
Reported(ReportedErrorInfo, Span),
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>;
pub type EvalToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>;
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8);
#[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 => {
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> {
self.0.kind
}
pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
}
#[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(),
}))
}
}
#[derive(Debug)]
pub enum InvalidProgramInfo<'tcx> {
TooGeneric,
AlreadyReported(ReportedErrorInfo),
Layout(layout::LayoutError<'tcx>),
FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
}
#[derive(Debug, Copy, Clone)]
pub enum CheckInAllocMsg {
MemoryAccessTest,
PointerArithmeticTest,
OffsetFromTest,
InboundsTest,
}
#[derive(Debug, Copy, Clone)]
pub enum CheckAlignMsg {
AccessedPtr,
BasedOn,
}
#[derive(Debug, Copy, Clone)]
pub enum InvalidMetaKind {
SliceTooBig,
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",
}))
}
}
#[derive(Debug, Clone, Copy)]
pub struct BadBytesAccess {
pub access: AllocRange,
pub bad: AllocRange,
}
#[derive(Debug)]
pub struct ScalarSizeMismatch {
pub target_size: u64,
pub data_size: u64,
}
#[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:?}")))
}
}
)*}
}
impl_into_diag_arg_through_debug! {
AllocId,
Pointer<AllocId>,
AllocRange,
}
#[derive(Debug)]
pub enum UndefinedBehaviorInfo<'tcx> {
Ub(String),
Custom(crate::error::CustomSubdiagnostic<'tcx>),
ValidationError(ValidationErrorInfo<'tcx>),
Unreachable,
BoundsCheckFailed { len: u64, index: u64 },
DivisionByZero,
RemainderByZero,
DivisionOverflow,
RemainderOverflow,
PointerArithOverflow,
ArithOverflow { intrinsic: Symbol },
ShiftOverflow { intrinsic: Symbol, shift_amount: Either<u128, i128> },
InvalidMeta(InvalidMetaKind),
UnterminatedCString(Pointer<AllocId>),
PointerUseAfterFree(AllocId, CheckInAllocMsg),
PointerOutOfBounds {
alloc_id: AllocId,
alloc_size: Size,
ptr_offset: i64,
inbounds_size: i64,
msg: CheckInAllocMsg,
},
DanglingIntPointer {
addr: u64,
inbounds_size: i64,
msg: CheckInAllocMsg,
},
AlignmentCheckFailed(Misalignment, CheckAlignMsg),
WriteToReadOnly(AllocId),
DerefFunctionPointer(AllocId),
DerefVTablePointer(AllocId),
InvalidBool(u8),
InvalidChar(u32),
InvalidTag(Scalar<AllocId>),
InvalidFunctionPointer(Pointer<AllocId>),
InvalidVTablePointer(Pointer<AllocId>),
InvalidVTableTrait {
vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
},
InvalidStr(std::str::Utf8Error),
InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>),
DeadLocal,
ScalarSizeMismatch(ScalarSizeMismatch),
UninhabitedEnumVariantWritten(VariantIdx),
UninhabitedEnumVariantRead(VariantIdx),
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> },
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 {
vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'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,
},
}
#[derive(Debug)]
pub enum UnsupportedOpInfo {
Unsupported(String),
UnsizedLocal,
ExternTypeField,
OverwritePartialPointer(Pointer<AllocId>),
ReadPartialPointer(Pointer<AllocId>),
ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>),
ThreadLocalStatic(DefId),
ExternStatic(DefId),
}
#[derive(Debug)]
pub enum ResourceExhaustionInfo {
StackFrameLimitReached,
MemoryExhausted,
AddressSpaceFull,
Interrupted,
}
pub trait MachineStopType: Any + fmt::Debug + Send {
fn diagnostic_message(&self) -> DiagMessage;
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> {
UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
Unsupported(UnsupportedOpInfo),
InvalidProgram(InvalidProgramInfo<'tcx>),
ResourceExhaustion(ResourceExhaustionInfo),
MachineStop(Box<dyn MachineStopType>),
}
impl InterpError<'_> {
pub fn formatted_string(&self) -> bool {
matches!(
self,
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. })
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
)
}
}
#[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)*))
};
}
#[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)*) };
}
#[derive(Debug)]
struct Guard;
impl Drop for Guard {
fn drop(&mut self) {
if !std::thread::panicking() {
panic!(
"an interpreter error got improperly discarded; use `discard_err()` if this is intentional"
);
}
}
}
#[derive(Debug)]
#[must_use]
pub struct InterpResult_<'tcx, T> {
res: Result<T, InterpErrorInfo<'tcx>>,
guard: Guard,
}
pub type InterpResult<'tcx, T = ()> = InterpResult_<'tcx, T>;
impl<'tcx, T> ops::Try for InterpResult_<'tcx, T> {
type Output = T;
type Residual = InterpResult_<'tcx, convert::Infallible>;
#[inline]
fn from_output(output: Self::Output) -> Self {
InterpResult_::new(Ok(output))
}
#[inline]
fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> {
match self.disarm() {
Ok(v) => ops::ControlFlow::Continue(v),
Err(e) => ops::ControlFlow::Break(InterpResult_::new(Err(e))),
}
}
}
impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> {
#[inline]
#[track_caller]
fn from_residual(residual: InterpResult_<'tcx, convert::Infallible>) -> Self {
match residual.disarm() {
Err(e) => Self::new(Err(e)),
}
}
}
impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpError<'tcx>>> for InterpResult_<'tcx, T> {
#[inline]
fn from_residual(ops::Yeet(e): ops::Yeet<InterpError<'tcx>>) -> Self {
Self::new(Err(e.into()))
}
}
impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> ops::FromResidual<Result<convert::Infallible, E>>
for InterpResult_<'tcx, T>
{
#[inline]
fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
match residual {
Err(e) => Self::new(Err(e.into())),
}
}
}
impl<'tcx, T, E: Into<InterpErrorInfo<'tcx>>> From<Result<T, E>> for InterpResult<'tcx, T> {
#[inline]
fn from(value: Result<T, E>) -> Self {
Self::new(value.map_err(|e| e.into()))
}
}
impl<'tcx, T, V: FromIterator<T>> FromIterator<InterpResult<'tcx, T>> for InterpResult<'tcx, V> {
fn from_iter<I: IntoIterator<Item = InterpResult<'tcx, T>>>(iter: I) -> Self {
Self::new(iter.into_iter().map(|x| x.disarm()).collect())
}
}
impl<'tcx, T> InterpResult_<'tcx, T> {
#[inline(always)]
fn new(res: Result<T, InterpErrorInfo<'tcx>>) -> Self {
Self { res, guard: Guard }
}
#[inline(always)]
fn disarm(self) -> Result<T, InterpErrorInfo<'tcx>> {
mem::forget(self.guard);
self.res
}
#[inline]
pub fn discard_err(self) -> Option<T> {
self.disarm().ok()
}
#[inline]
pub fn report_err(self) -> Result<T, InterpErrorInfo<'tcx>> {
self.disarm()
}
#[inline]
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> InterpResult<'tcx, U> {
InterpResult_::new(self.disarm().map(f))
}
#[inline]
pub fn map_err(
self,
f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>,
) -> InterpResult<'tcx, T> {
InterpResult_::new(self.disarm().map_err(f))
}
#[inline]
pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> {
InterpResult_::new(self.disarm().inspect_err(f))
}
#[inline]
#[track_caller]
pub fn unwrap(self) -> T {
self.disarm().unwrap()
}
#[inline]
#[track_caller]
pub fn unwrap_or_else(self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> T) -> T {
self.disarm().unwrap_or_else(f)
}
#[inline]
#[track_caller]
pub fn expect(self, msg: &str) -> T {
self.disarm().expect(msg)
}
#[inline]
pub fn and_then<U>(self, f: impl FnOnce(T) -> InterpResult<'tcx, U>) -> InterpResult<'tcx, U> {
InterpResult_::new(self.disarm().and_then(|t| f(t).disarm()))
}
#[inline]
pub fn and<U>(self, other: InterpResult<'tcx, U>) -> InterpResult<'tcx, (T, U)> {
match self.disarm() {
Ok(t) => interp_ok((t, other?)),
Err(e) => {
drop(other.disarm());
InterpResult_::new(Err(e))
}
}
}
}
#[inline(always)]
pub fn interp_ok<'tcx, T>(x: T) -> InterpResult<'tcx, T> {
InterpResult_::new(Ok(x))
}