4 use rustc_errors::{DiagnosticBuilder, ErrorReported};
6 use rustc_middle::mir::AssertKind;
7 use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
8 use rustc_span::{Span, Symbol};
11 use crate::interpret::{
12 struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine,
15 /// The CTFE machine has some custom error kinds.
16 #[derive(Clone, Debug)]
17 pub enum ConstEvalErrKind {
22 AssertFailure(AssertKind<ConstInt>),
23 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
27 // The errors become `MachineStop` with plain strings when being raised.
28 // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
30 impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
31 fn into(self) -> InterpErrorInfo<'tcx> {
32 err_machine_stop!(self.to_string()).into()
36 impl fmt::Display for ConstEvalErrKind {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 use self::ConstEvalErrKind::*;
40 NeedsRfc(ref msg) => {
41 write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
46 "cannot cast pointer to integer because it was not created by cast from integer"
49 ConstAccessesStatic => write!(f, "constant accesses static"),
51 write!(f, "modifying a static's initial value from another static's initializer")
53 AssertFailure(ref msg) => write!(f, "{:?}", msg),
54 Panic { msg, line, col, file } => {
55 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
57 Abort(ref msg) => write!(f, "{}", msg),
62 impl Error for ConstEvalErrKind {}
64 /// When const-evaluation errors, this type is constructed with the resulting information,
65 /// and then used to emit the error as a lint or hard error.
67 pub struct ConstEvalErr<'tcx> {
69 pub error: InterpError<'tcx>,
70 pub stacktrace: Vec<FrameInfo<'tcx>>,
73 impl<'tcx> ConstEvalErr<'tcx> {
74 /// Turn an interpreter error into something to report to the user.
75 /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
76 /// Should be called only if the error is actually going to to be reported!
77 pub fn new<'mir, M: Machine<'mir, 'tcx>>(
78 ecx: &InterpCx<'mir, 'tcx, M>,
79 error: InterpErrorInfo<'tcx>,
81 ) -> ConstEvalErr<'tcx>
85 error.print_backtrace();
86 let stacktrace = ecx.generate_stacktrace();
88 error: error.into_kind(),
90 span: span.unwrap_or_else(|| ecx.cur_span()),
98 emit: impl FnOnce(DiagnosticBuilder<'_>),
100 self.struct_generic(tcx, message, emit, None)
103 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
104 self.struct_error(tcx, message, |mut e| e.emit())
107 pub fn report_as_lint(
111 lint_root: hir::HirId,
117 |mut lint: DiagnosticBuilder<'_>| {
119 if let Some(span) = span {
120 let primary_spans = lint.span.primary_spans().to_vec();
121 // point at the actual error as the primary span
122 lint.replace_span_with(span);
123 // point to the `const` statement as a secondary span
124 // they don't have any label
125 for sp in primary_spans {
127 lint.span_label(sp, "");
137 /// Create a diagnostic for this const eval error.
139 /// Sets the message passed in via `message` and adds span labels with detailed error
140 /// information before handing control back to `emit` to do any final processing.
141 /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
142 /// function to dispose of the diagnostic properly.
144 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
145 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
150 emit: impl FnOnce(DiagnosticBuilder<'_>),
151 lint_root: Option<hir::HirId>,
153 let must_error = match self.error {
154 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
155 return ErrorHandled::TooGeneric;
157 err_inval!(AlreadyReported(error_reported)) => {
158 return ErrorHandled::Reported(error_reported);
160 // We must *always* hard error on these, even if the caller wants just a lint.
161 err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
164 trace!("reporting const eval failure at {:?}", self.span);
166 let err_msg = match &self.error {
167 InterpError::MachineStop(msg) => {
168 // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
169 // Should be turned into a string by now.
170 msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
172 err => err.to_string(),
175 let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
176 if let Some(span_msg) = span_msg {
177 err.span_label(self.span, span_msg);
179 // Add spans for the stacktrace. Don't print a single-line backtrace though.
180 if self.stacktrace.len() > 1 {
181 for frame_info in &self.stacktrace {
182 err.span_label(frame_info.span, frame_info.to_string());
185 // Let the caller finish the job.
190 // The `message` makes little sense here, this is a more serious error than the
191 // caller thinks anyway.
192 // See <https://github.com/rust-lang/rust/pull/63152>.
193 finish(struct_error(tcx, &err_msg), None);
194 ErrorHandled::Reported(ErrorReported)
197 if let Some(lint_root) = lint_root {
203 .find_map(|frame| frame.lint_root)
204 .unwrap_or(lint_root);
205 tcx.struct_span_lint_hir(
206 rustc_session::lint::builtin::CONST_ERR,
209 |lint| finish(lint.build(message), Some(err_msg)),
213 // Report as hard error.
214 finish(struct_error(tcx, message), Some(err_msg));
215 ErrorHandled::Reported(ErrorReported)