4 use rustc_errors::Diagnostic;
5 use rustc_middle::mir::AssertKind;
6 use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
7 use rustc_span::{Span, Symbol};
10 use crate::interpret::{
11 struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
15 /// The CTFE machine has some custom error kinds.
16 #[derive(Clone, Debug)]
17 pub enum ConstEvalErrKind {
20 AssertFailure(AssertKind<ConstInt>),
21 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
25 impl MachineStopType for ConstEvalErrKind {}
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).into()
36 impl fmt::Display for ConstEvalErrKind {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 use self::ConstEvalErrKind::*;
40 ConstAccessesStatic => write!(f, "constant accesses static"),
42 write!(f, "modifying a static's initial value from another static's initializer")
44 AssertFailure(ref msg) => write!(f, "{:?}", msg),
45 Panic { msg, line, col, file } => {
46 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
48 Abort(ref msg) => write!(f, "{}", msg),
53 impl Error for ConstEvalErrKind {}
55 /// When const-evaluation errors, this type is constructed with the resulting information,
56 /// and then used to emit the error as a lint or hard error.
58 pub(super) struct ConstEvalErr<'tcx> {
60 pub error: InterpError<'tcx>,
61 pub stacktrace: Vec<FrameInfo<'tcx>>,
64 impl<'tcx> ConstEvalErr<'tcx> {
65 /// Turn an interpreter error into something to report to the user.
66 /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
67 /// Should be called only if the error is actually going to be reported!
68 pub fn new<'mir, M: Machine<'mir, 'tcx>>(
69 ecx: &InterpCx<'mir, 'tcx, M>,
70 error: InterpErrorInfo<'tcx>,
72 ) -> ConstEvalErr<'tcx>
76 error.print_backtrace();
77 let mut stacktrace = ecx.generate_stacktrace();
78 // Filter out `requires_caller_location` frames.
79 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
80 // If `span` is missing, use topmost remaining frame, or else the "root" span from `ecx.tcx`.
81 let span = span.or_else(|| stacktrace.first().map(|f| f.span)).unwrap_or(ecx.tcx.span);
82 ConstEvalErr { error: error.into_kind(), stacktrace, span }
85 pub(super) fn report(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
86 self.report_decorated(tcx, message, |_| {})
89 /// Create a diagnostic for this const eval error.
91 /// Sets the message passed in via `message` and adds span labels with detailed error
92 /// information before handing control back to `decorate` to do any final annotations,
93 /// after which the diagnostic is emitted.
95 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
96 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
97 #[instrument(skip(self, tcx, decorate), level = "debug")]
98 pub(super) fn report_decorated(
102 decorate: impl FnOnce(&mut Diagnostic),
104 let finish = |err: &mut Diagnostic, span_msg: Option<String>| {
105 trace!("reporting const eval failure at {:?}", self.span);
106 if let Some(span_msg) = span_msg {
107 err.span_label(self.span, span_msg);
109 // Add some more context for select error types.
111 InterpError::Unsupported(
112 UnsupportedOpInfo::ReadPointerAsBytes
113 | UnsupportedOpInfo::PartialPointerOverwrite(_)
114 | UnsupportedOpInfo::PartialPointerCopy(_),
116 err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
117 err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
121 // Add spans for the stacktrace. Don't print a single-line backtrace though.
122 if self.stacktrace.len() > 1 {
123 // Helper closure to print duplicated lines.
124 let mut flush_last_line = |last_frame, times| {
125 if let Some((line, span)) = last_frame {
126 err.span_note(span, &line);
127 // Don't print [... additional calls ...] if the number of lines is small
130 err.span_note(span, &line);
135 format!("[... {} additional calls {} ...]", times, &line),
141 let mut last_frame = None;
143 for frame_info in &self.stacktrace {
144 let frame = (frame_info.to_string(), frame_info.span);
145 if last_frame.as_ref() == Some(&frame) {
148 flush_last_line(last_frame, times);
149 last_frame = Some(frame);
153 flush_last_line(last_frame, times);
155 // Let the caller attach any additional information it wants.
159 debug!("self.error: {:?}", self.error);
160 // Special handling for certain errors
162 // Don't emit a new diagnostic for these errors
163 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
164 return ErrorHandled::TooGeneric;
166 err_inval!(AlreadyReported(error_reported)) => {
167 return ErrorHandled::Reported(*error_reported);
169 err_inval!(Layout(LayoutError::SizeOverflow(_))) => {
170 // We must *always* hard error on these, even if the caller wants just a lint.
171 // The `message` makes little sense here, this is a more serious error than the
172 // caller thinks anyway.
173 // See <https://github.com/rust-lang/rust/pull/63152>.
174 let mut err = struct_error(tcx, &self.error.to_string());
175 finish(&mut err, None);
176 return ErrorHandled::Reported(err.emit());
181 let err_msg = self.error.to_string();
183 // Report as hard error.
184 let mut err = struct_error(tcx, message);
185 finish(&mut err, Some(err_msg));
186 ErrorHandled::Reported(err.emit())