1 use std::mem;
2
3 use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg};
4 use rustc_middle::mir::AssertKind;
5 use rustc_middle::ty::TyCtxt;
6 use rustc_middle::ty::{layout::LayoutError, ConstInt};
7 use rustc_span::source_map::Spanned;
8 use rustc_span::{ErrorGuaranteed, Span, Symbol};
9
10 use super::InterpCx;
11 use crate::errors::{self, FrameNote, ReportErrorExt};
12 use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType};
13
14 /// The CTFE machine has some custom error kinds.
15 #[derive(Clone, Debug)]
16 pub enum ConstEvalErrKind {
17 ConstAccessesStatic,
18 ModifiedGlobal,
19 AssertFailure(AssertKind<ConstInt>),
20 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
21 Abort(String),
22 }
23
24 impl MachineStopType for ConstEvalErrKind {
diagnostic_message(&self) -> DiagnosticMessage25 fn diagnostic_message(&self) -> DiagnosticMessage {
26 use crate::fluent_generated::*;
27 use ConstEvalErrKind::*;
28 match self {
29 ConstAccessesStatic => const_eval_const_accesses_static,
30 ModifiedGlobal => const_eval_modified_global,
31 Panic { .. } => const_eval_panic,
32 AssertFailure(x) => x.diagnostic_message(),
33 Abort(msg) => msg.to_string().into(),
34 }
35 }
add_args( self: Box<Self>, adder: &mut dyn FnMut(std::borrow::Cow<'static, str>, DiagnosticArgValue<'static>), )36 fn add_args(
37 self: Box<Self>,
38 adder: &mut dyn FnMut(std::borrow::Cow<'static, str>, DiagnosticArgValue<'static>),
39 ) {
40 use ConstEvalErrKind::*;
41 match *self {
42 ConstAccessesStatic | ModifiedGlobal | Abort(_) => {}
43 AssertFailure(kind) => kind.add_args(adder),
44 Panic { msg, line, col, file } => {
45 adder("msg".into(), msg.into_diagnostic_arg());
46 adder("file".into(), file.into_diagnostic_arg());
47 adder("line".into(), line.into_diagnostic_arg());
48 adder("col".into(), col.into_diagnostic_arg());
49 }
50 }
51 }
52 }
53
54 // The errors become `MachineStop` with plain strings when being raised.
55 // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
56 // handle these.
57 impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
into(self) -> InterpErrorInfo<'tcx>58 fn into(self) -> InterpErrorInfo<'tcx> {
59 err_machine_stop!(self).into()
60 }
61 }
62
get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>( ecx: &InterpCx<'mir, 'tcx, M>, ) -> (Span, Vec<errors::FrameNote>) where 'tcx: 'mir,63 pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>(
64 ecx: &InterpCx<'mir, 'tcx, M>,
65 ) -> (Span, Vec<errors::FrameNote>)
66 where
67 'tcx: 'mir,
68 {
69 let mut stacktrace = ecx.generate_stacktrace();
70 // Filter out `requires_caller_location` frames.
71 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
72 let span = stacktrace.first().map(|f| f.span).unwrap_or(ecx.tcx.span);
73
74 let mut frames = Vec::new();
75
76 // Add notes to the backtrace. Don't print a single-line backtrace though.
77 if stacktrace.len() > 1 {
78 // Helper closure to print duplicated lines.
79 let mut add_frame = |mut frame: errors::FrameNote| {
80 frames.push(errors::FrameNote { times: 0, ..frame.clone() });
81 // Don't print [... additional calls ...] if the number of lines is small
82 if frame.times < 3 {
83 let times = frame.times;
84 frame.times = 0;
85 frames.extend(std::iter::repeat(frame).take(times as usize));
86 } else {
87 frames.push(frame);
88 }
89 };
90
91 let mut last_frame: Option<errors::FrameNote> = None;
92 for frame_info in &stacktrace {
93 let frame = frame_info.as_note(*ecx.tcx);
94 match last_frame.as_mut() {
95 Some(last_frame)
96 if last_frame.span == frame.span
97 && last_frame.where_ == frame.where_
98 && last_frame.instance == frame.instance =>
99 {
100 last_frame.times += 1;
101 }
102 Some(last_frame) => {
103 add_frame(mem::replace(last_frame, frame));
104 }
105 None => {
106 last_frame = Some(frame);
107 }
108 }
109 }
110 if let Some(frame) = last_frame {
111 add_frame(frame);
112 }
113 }
114
115 (span, frames)
116 }
117
118 /// Create a diagnostic for a const eval error.
119 ///
120 /// This will use the `mk` function for creating the error which will get passed labels according to
121 /// the `InterpError` and the span and a stacktrace of current execution according to
122 /// `get_span_and_frames`.
report<'tcx, C, F, E>( tcx: TyCtxt<'tcx>, error: InterpError<'tcx>, span: Option<Span>, get_span_and_frames: C, mk: F, ) -> ErrorHandled where C: FnOnce() -> (Span, Vec<FrameNote>), F: FnOnce(Span, Vec<FrameNote>) -> E, E: IntoDiagnostic<'tcx, ErrorGuaranteed>,123 pub(super) fn report<'tcx, C, F, E>(
124 tcx: TyCtxt<'tcx>,
125 error: InterpError<'tcx>,
126 span: Option<Span>,
127 get_span_and_frames: C,
128 mk: F,
129 ) -> ErrorHandled
130 where
131 C: FnOnce() -> (Span, Vec<FrameNote>),
132 F: FnOnce(Span, Vec<FrameNote>) -> E,
133 E: IntoDiagnostic<'tcx, ErrorGuaranteed>,
134 {
135 // Special handling for certain errors
136 match error {
137 // Don't emit a new diagnostic for these errors
138 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
139 ErrorHandled::TooGeneric
140 }
141 err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(error_reported),
142 err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => {
143 // We must *always* hard error on these, even if the caller wants just a lint.
144 // The `message` makes little sense here, this is a more serious error than the
145 // caller thinks anyway.
146 // See <https://github.com/rust-lang/rust/pull/63152>.
147 let (our_span, frames) = get_span_and_frames();
148 let span = span.unwrap_or(our_span);
149 let mut err =
150 tcx.sess.create_err(Spanned { span, node: layout_error.into_diagnostic() });
151 err.code(rustc_errors::error_code!(E0080));
152 let Some((mut err, handler)) = err.into_diagnostic() else {
153 panic!("did not emit diag");
154 };
155 for frame in frames {
156 err.eager_subdiagnostic(handler, frame);
157 }
158
159 ErrorHandled::Reported(handler.emit_diagnostic(&mut err).unwrap().into())
160 }
161 _ => {
162 // Report as hard error.
163 let (our_span, frames) = get_span_and_frames();
164 let span = span.unwrap_or(our_span);
165 let err = mk(span, frames);
166 let mut err = tcx.sess.create_err(err);
167
168 let msg = error.diagnostic_message();
169 error.add_args(&tcx.sess.parse_sess.span_diagnostic, &mut err);
170
171 // Use *our* span to label the interp error
172 err.span_label(our_span, msg);
173 ErrorHandled::Reported(err.emit().into())
174 }
175 }
176 }
177