• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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