2019-12-25 01:04:32 +01:00
|
|
|
use std::error::Error;
|
|
|
|
use std::fmt;
|
|
|
|
|
2022-01-27 09:44:25 +00:00
|
|
|
use rustc_errors::Diagnostic;
|
2020-08-09 15:37:32 +02:00
|
|
|
use rustc_hir as hir;
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::AssertKind;
|
2020-08-09 15:37:32 +02:00
|
|
|
use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
|
2020-06-01 11:17:38 +02:00
|
|
|
use rustc_span::{Span, Symbol};
|
2020-02-09 16:51:36 +01:00
|
|
|
|
2019-12-25 01:06:51 +01:00
|
|
|
use super::InterpCx;
|
2020-08-09 15:37:32 +02:00
|
|
|
use crate::interpret::{
|
2021-05-25 20:54:59 -05:00
|
|
|
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
|
2020-08-09 15:37:32 +02:00
|
|
|
};
|
2020-02-08 22:21:20 +01:00
|
|
|
|
|
|
|
/// The CTFE machine has some custom error kinds.
|
2019-12-25 01:04:32 +01:00
|
|
|
#[derive(Clone, Debug)]
|
2020-02-08 22:21:20 +01:00
|
|
|
pub enum ConstEvalErrKind {
|
2019-12-25 01:04:32 +01:00
|
|
|
NeedsRfc(String),
|
|
|
|
ConstAccessesStatic,
|
2020-03-21 19:19:10 +01:00
|
|
|
ModifiedGlobal,
|
2020-06-19 18:57:15 +02:00
|
|
|
AssertFailure(AssertKind<ConstInt>),
|
2020-02-09 16:51:36 +01:00
|
|
|
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
|
2020-12-05 17:32:19 +01:00
|
|
|
Abort(String),
|
2019-12-25 01:04:32 +01:00
|
|
|
}
|
|
|
|
|
2021-05-25 20:54:59 -05:00
|
|
|
impl MachineStopType for ConstEvalErrKind {
|
|
|
|
fn is_hard_err(&self) -> bool {
|
2021-11-06 01:31:32 +01:00
|
|
|
matches!(self, Self::Panic { .. })
|
2021-05-25 20:54:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 22:21:20 +01:00
|
|
|
// The errors become `MachineStop` with plain strings when being raised.
|
2020-03-29 15:24:45 +02:00
|
|
|
// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
|
2020-02-08 22:21:20 +01:00
|
|
|
// handle these.
|
|
|
|
impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
|
2019-12-25 01:04:32 +01:00
|
|
|
fn into(self) -> InterpErrorInfo<'tcx> {
|
2021-05-25 20:54:59 -05:00
|
|
|
err_machine_stop!(self).into()
|
2019-12-25 01:04:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 22:21:20 +01:00
|
|
|
impl fmt::Display for ConstEvalErrKind {
|
2019-12-25 01:04:32 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-02-08 22:21:20 +01:00
|
|
|
use self::ConstEvalErrKind::*;
|
2019-12-25 01:04:32 +01:00
|
|
|
match *self {
|
|
|
|
NeedsRfc(ref msg) => {
|
|
|
|
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
|
|
|
|
}
|
|
|
|
ConstAccessesStatic => write!(f, "constant accesses static"),
|
2020-03-22 09:23:19 +01:00
|
|
|
ModifiedGlobal => {
|
|
|
|
write!(f, "modifying a static's initial value from another static's initializer")
|
|
|
|
}
|
2020-02-09 16:51:36 +01:00
|
|
|
AssertFailure(ref msg) => write!(f, "{:?}", msg),
|
|
|
|
Panic { msg, line, col, file } => {
|
|
|
|
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
|
|
|
|
}
|
2020-12-05 18:39:10 +01:00
|
|
|
Abort(ref msg) => write!(f, "{}", msg),
|
2019-12-25 01:04:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 22:21:20 +01:00
|
|
|
impl Error for ConstEvalErrKind {}
|
2019-12-25 01:06:51 +01:00
|
|
|
|
2020-08-09 15:37:32 +02:00
|
|
|
/// When const-evaluation errors, this type is constructed with the resulting information,
|
|
|
|
/// and then used to emit the error as a lint or hard error.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ConstEvalErr<'tcx> {
|
|
|
|
pub span: Span,
|
|
|
|
pub error: InterpError<'tcx>,
|
|
|
|
pub stacktrace: Vec<FrameInfo<'tcx>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> ConstEvalErr<'tcx> {
|
|
|
|
/// Turn an interpreter error into something to report to the user.
|
|
|
|
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
|
|
|
/// Should be called only if the error is actually going to to be reported!
|
|
|
|
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();
|
2021-02-14 00:00:00 +00:00
|
|
|
ConstEvalErr {
|
|
|
|
error: error.into_kind(),
|
|
|
|
stacktrace,
|
|
|
|
span: span.unwrap_or_else(|| ecx.cur_span()),
|
|
|
|
}
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn struct_error(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxtAt<'tcx>,
|
|
|
|
message: &str,
|
2022-01-27 09:44:25 +00:00
|
|
|
decorate: impl FnOnce(&mut Diagnostic),
|
2020-08-09 15:37:32 +02:00
|
|
|
) -> ErrorHandled {
|
2022-01-27 09:44:25 +00:00
|
|
|
self.struct_generic(tcx, message, decorate, None)
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
|
2022-01-27 09:44:25 +00:00
|
|
|
self.struct_error(tcx, message, |_| {})
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn report_as_lint(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxtAt<'tcx>,
|
|
|
|
message: &str,
|
|
|
|
lint_root: hir::HirId,
|
|
|
|
span: Option<Span>,
|
|
|
|
) -> ErrorHandled {
|
|
|
|
self.struct_generic(
|
|
|
|
tcx,
|
|
|
|
message,
|
2022-01-27 09:44:25 +00:00
|
|
|
|lint: &mut Diagnostic| {
|
2020-08-09 15:37:32 +02:00
|
|
|
// Apply the span.
|
|
|
|
if let Some(span) = span {
|
|
|
|
let primary_spans = lint.span.primary_spans().to_vec();
|
|
|
|
// point at the actual error as the primary span
|
|
|
|
lint.replace_span_with(span);
|
|
|
|
// point to the `const` statement as a secondary span
|
|
|
|
// they don't have any label
|
|
|
|
for sp in primary_spans {
|
|
|
|
if sp != span {
|
|
|
|
lint.span_label(sp, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Some(lint_root),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a diagnostic for this const eval error.
|
|
|
|
///
|
|
|
|
/// Sets the message passed in via `message` and adds span labels with detailed error
|
2022-01-27 09:44:25 +00:00
|
|
|
/// information before handing control back to `decorate` to do any final annotations,
|
|
|
|
/// after which the diagnostic is emitted.
|
2020-08-09 15:37:32 +02:00
|
|
|
///
|
|
|
|
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
|
|
|
|
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
|
2022-06-02 19:42:29 +02:00
|
|
|
#[instrument(skip(self, tcx, decorate, lint_root), level = "debug")]
|
2020-08-09 15:37:32 +02:00
|
|
|
fn struct_generic(
|
|
|
|
&self,
|
|
|
|
tcx: TyCtxtAt<'tcx>,
|
|
|
|
message: &str,
|
2022-01-27 09:44:25 +00:00
|
|
|
decorate: impl FnOnce(&mut Diagnostic),
|
2021-06-15 19:16:10 -04:00
|
|
|
lint_root: Option<hir::HirId>,
|
2020-08-09 15:37:32 +02:00
|
|
|
) -> ErrorHandled {
|
2022-01-27 09:44:25 +00:00
|
|
|
let finish = |err: &mut Diagnostic, span_msg: Option<String>| {
|
2021-05-25 20:54:59 -05:00
|
|
|
trace!("reporting const eval failure at {:?}", self.span);
|
2020-08-09 15:37:32 +02:00
|
|
|
if let Some(span_msg) = span_msg {
|
|
|
|
err.span_label(self.span, span_msg);
|
|
|
|
}
|
|
|
|
// Add spans for the stacktrace. Don't print a single-line backtrace though.
|
|
|
|
if self.stacktrace.len() > 1 {
|
2022-01-11 17:55:56 -08:00
|
|
|
// Helper closure to print duplicated lines.
|
|
|
|
let mut flush_last_line = |last_frame, times| {
|
|
|
|
if let Some((line, span)) = last_frame {
|
|
|
|
err.span_label(span, &line);
|
|
|
|
// Don't print [... additional calls ...] if the number of lines is small
|
|
|
|
if times < 3 {
|
|
|
|
for _ in 0..times {
|
|
|
|
err.span_label(span, &line);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err.span_label(
|
|
|
|
span,
|
|
|
|
format!("[... {} additional calls {} ...]", times, &line),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut last_frame = None;
|
|
|
|
let mut times = 0;
|
2020-08-09 15:37:32 +02:00
|
|
|
for frame_info in &self.stacktrace {
|
2022-01-11 17:55:56 -08:00
|
|
|
let frame = (frame_info.to_string(), frame_info.span);
|
|
|
|
if last_frame.as_ref() == Some(&frame) {
|
|
|
|
times += 1;
|
|
|
|
} else {
|
|
|
|
flush_last_line(last_frame, times);
|
|
|
|
last_frame = Some(frame);
|
|
|
|
times = 0;
|
|
|
|
}
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
2022-01-11 17:55:56 -08:00
|
|
|
flush_last_line(last_frame, times);
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
2022-01-27 09:44:25 +00:00
|
|
|
// Let the caller attach any additional information it wants.
|
|
|
|
decorate(err);
|
2020-08-09 15:37:32 +02:00
|
|
|
};
|
|
|
|
|
2022-06-02 19:42:29 +02:00
|
|
|
debug!("self.error: {:?}", self.error);
|
2021-05-25 20:54:59 -05:00
|
|
|
// Special handling for certain errors
|
|
|
|
match &self.error {
|
|
|
|
// Don't emit a new diagnostic for these errors
|
|
|
|
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(_))) => {
|
|
|
|
// We must *always* hard error on these, even if the caller wants just a lint.
|
|
|
|
// The `message` makes little sense here, this is a more serious error than the
|
|
|
|
// caller thinks anyway.
|
|
|
|
// See <https://github.com/rust-lang/rust/pull/63152>.
|
2022-01-27 09:44:25 +00:00
|
|
|
let mut err = struct_error(tcx, &self.error.to_string());
|
|
|
|
finish(&mut err, None);
|
|
|
|
return ErrorHandled::Reported(err.emit());
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
2021-05-25 20:54:59 -05:00
|
|
|
_ => {}
|
|
|
|
};
|
|
|
|
|
|
|
|
let err_msg = self.error.to_string();
|
|
|
|
|
|
|
|
// Regular case - emit a lint.
|
|
|
|
if let Some(lint_root) = lint_root {
|
|
|
|
// Report as lint.
|
|
|
|
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,
|
2022-01-27 09:44:25 +00:00
|
|
|
|lint| {
|
|
|
|
let mut lint = lint.build(message);
|
|
|
|
finish(&mut lint, Some(err_msg));
|
|
|
|
lint.emit();
|
|
|
|
},
|
2021-05-25 20:54:59 -05:00
|
|
|
);
|
|
|
|
ErrorHandled::Linted
|
|
|
|
} else {
|
|
|
|
// Report as hard error.
|
2022-01-27 09:44:25 +00:00
|
|
|
let mut err = struct_error(tcx, message);
|
|
|
|
finish(&mut err, Some(err_msg));
|
|
|
|
ErrorHandled::Reported(err.emit())
|
2020-08-09 15:37:32 +02:00
|
|
|
}
|
|
|
|
}
|
2019-12-25 01:06:51 +01:00
|
|
|
}
|