use super::{AllocId, AllocRange, Pointer, Scalar};
use crate::error;
use crate::mir::{ConstAlloc, ConstValue};
use crate::query::TyCtxtAt;
use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
use rustc_data_structures::sync::Lock;
use rustc_errors::{
struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
IntoDiagnosticArg,
};
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
use std::borrow::Cow;
use std::{any::Any, backtrace::Backtrace, fmt};
#[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_err(&self, tcx: TyCtxt<'_>) -> ErrorGuaranteed {
match self {
&ErrorHandled::Reported(err, span) => {
if !err.is_tainted_by_errors && !span.is_dummy() {
tcx.sess.emit_err(error::ErroneousConstant { span });
}
err.error
}
&ErrorHandled::TooGeneric(span) => tcx.sess.delay_span_bug(
span,
"encountered TooGeneric error when monomorphic data was expected",
),
}
}
pub fn emit_note(&self, tcx: TyCtxt<'_>) {
match self {
&ErrorHandled::Reported(err, span) => {
if !err.is_tainted_by_errors && !span.is_dummy() {
tcx.sess.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 }
}
}
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 EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
pub fn struct_error<'tcx>(
tcx: TyCtxtAt<'tcx>,
msg: &str,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
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> {
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(),
}))
}
}
#[derive(Debug)]
pub enum InvalidProgramInfo<'tcx> {
TooGeneric,
AlreadyReported(ReportedErrorInfo),
Layout(layout::LayoutError<'tcx>),
FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
ConstPropNonsense,
}
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum CheckInAllocMsg {
DerefTest,
MemoryAccessTest,
PointerArithmeticTest,
OffsetFromTest,
InboundsTest,
}
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum InvalidMetaKind {
SliceTooBig,
TooBig,
}
impl IntoDiagnosticArg for InvalidMetaKind {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::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,
}
macro_rules! impl_into_diagnostic_arg_through_debug {
($($ty:ty),*$(,)?) => {$(
impl IntoDiagnosticArg for $ty {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
}
}
)*}
}
impl_into_diagnostic_arg_through_debug! {
AllocId,
Pointer,
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,
InvalidMeta(InvalidMetaKind),
UnterminatedCString(Pointer),
PointerUseAfterFree(AllocId, CheckInAllocMsg),
PointerOutOfBounds {
alloc_id: AllocId,
alloc_size: Size,
ptr_offset: i64,
ptr_size: Size,
msg: CheckInAllocMsg,
},
DanglingIntPointer(u64, CheckInAllocMsg),
AlignmentCheckFailed { required: Align, has: Align },
WriteToReadOnly(AllocId),
DerefFunctionPointer(AllocId),
DerefVTablePointer(AllocId),
InvalidBool(u8),
InvalidChar(u32),
InvalidTag(Scalar),
InvalidFunctionPointer(Pointer),
InvalidVTablePointer(Pointer),
InvalidStr(std::str::Utf8Error),
InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>),
DeadLocal,
ScalarSizeMismatch(ScalarSizeMismatch),
UninhabitedEnumVariantWritten(VariantIdx),
UninhabitedEnumVariantRead(VariantIdx),
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,
Box,
}
impl IntoDiagnosticArg for PointerKind {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::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> },
PtrToStatic { ptr_kind: PointerKind },
PtrToMut { ptr_kind: PointerKind },
MutableRefInConst,
NullFnPtr,
NeverVal,
NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
PtrOutOfRange { range: WrappingRange, max_value: u128 },
OutOfRange { value: String, range: WrappingRange, max_value: u128 },
UnsafeCell,
UninhabitedVal { ty: Ty<'tcx> },
InvalidEnumTag { value: String },
UninhabitedEnumVariant,
Uninit { expected: ExpectedKind },
InvalidVTablePtr { value: String },
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,
OverwritePartialPointer(Pointer<AllocId>),
ReadPartialPointer(Pointer<AllocId>),
ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>),
ThreadLocalStatic(DefId),
ReadExternStatic(DefId),
}
#[derive(Debug)]
pub enum ResourceExhaustionInfo {
StackFrameLimitReached,
MemoryExhausted,
AddressSpaceFull,
}
pub trait MachineStopType: Any + fmt::Debug + Send {
fn diagnostic_message(&self) -> DiagnosticMessage;
fn add_args(
self: Box<Self>,
adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
);
}
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>),
}
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
impl InterpError<'_> {
pub fn formatted_string(&self) -> bool {
matches!(
self,
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. })
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
)
}
}