use std::error::Error;
use std::fmt;
use rustc_errors::{DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_middle::mir::AssertKind;
use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
use rustc_span::{Span, Symbol};
use super::InterpCx;
use crate::interpret::{
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine,
};
#[derive(Clone, Debug)]
pub enum ConstEvalErrKind {
NeedsRfc(String),
PtrToIntCast,
ConstAccessesStatic,
ModifiedGlobal,
AssertFailure(AssertKind<ConstInt>),
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
Abort(String),
}
impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
fn into(self) -> InterpErrorInfo<'tcx> {
err_machine_stop!(self.to_string()).into()
}
}
impl fmt::Display for ConstEvalErrKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ConstEvalErrKind::*;
match *self {
NeedsRfc(ref msg) => {
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
}
PtrToIntCast => {
write!(
f,
"cannot cast pointer to integer because it was not created by cast from integer"
)
}
ConstAccessesStatic => write!(f, "constant accesses static"),
ModifiedGlobal => {
write!(f, "modifying a static's initial value from another static's initializer")
}
AssertFailure(ref msg) => write!(f, "{:?}", msg),
Panic { msg, line, col, file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
}
Abort(ref msg) => write!(f, "{}", msg),
}
}
}
impl Error for ConstEvalErrKind {}
#[derive(Debug)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub error: InterpError<'tcx>,
pub stacktrace: Vec<FrameInfo<'tcx>>,
}
impl<'tcx> ConstEvalErr<'tcx> {
pub fn new<'mir, M: Machine<'mir, 'tcx>>(
ecx: &InterpCx<'mir, 'tcx, M>,
error: InterpErrorInfo<'tcx>,
span: Option<Span>,
) -> ConstEvalErr<'tcx>
where
'tcx: 'mir,
{
error.print_backtrace();
let stacktrace = ecx.generate_stacktrace();
ConstEvalErr {
error: error.into_kind(),
stacktrace,
span: span.unwrap_or_else(|| ecx.cur_span()),
}
}
pub fn struct_error(
&self,
tcx: TyCtxtAt<'tcx>,
message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
) -> ErrorHandled {
self.struct_generic(tcx, message, emit, None)
}
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
self.struct_error(tcx, message, |mut e| e.emit())
}
pub fn report_as_lint(
&self,
tcx: TyCtxtAt<'tcx>,
message: &str,
lint_root: hir::HirId,
span: Option<Span>,
) -> ErrorHandled {
self.struct_generic(
tcx,
message,
|mut lint: DiagnosticBuilder<'_>| {
if let Some(span) = span {
let primary_spans = lint.span.primary_spans().to_vec();
lint.replace_span_with(span);
for sp in primary_spans {
if sp != span {
lint.span_label(sp, "");
}
}
}
lint.emit();
},
Some(lint_root),
)
}
fn struct_generic(
&self,
tcx: TyCtxtAt<'tcx>,
message: &str,
emit: impl FnOnce(DiagnosticBuilder<'_>),
lint_root: Option<hir::HirId>,
) -> ErrorHandled {
let must_error = match self.error {
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
return ErrorHandled::TooGeneric;
}
err_inval!(AlreadyReported(error_reported)) => {
return ErrorHandled::Reported(error_reported);
}
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
_ => false,
};
trace!("reporting const eval failure at {:?}", self.span);
let err_msg = match &self.error {
InterpError::MachineStop(msg) => {
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
}
err => err.to_string(),
};
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
if let Some(span_msg) = span_msg {
err.span_label(self.span, span_msg);
}
if self.stacktrace.len() > 1 {
for frame_info in &self.stacktrace {
err.span_label(frame_info.span, frame_info.to_string());
}
}
emit(err)
};
if must_error {
finish(struct_error(tcx, &err_msg), None);
ErrorHandled::Reported(ErrorReported)
} else {
if let Some(lint_root) = lint_root {
let hir_id = self
.stacktrace
.iter()
.rev()
.find_map(|frame| frame.lint_root)
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
|lint| finish(lint.build(message), Some(err_msg)),
);
ErrorHandled::Linted
} else {
finish(struct_error(tcx, message), Some(err_msg));
ErrorHandled::Reported(ErrorReported)
}
}
}
}