]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_const_eval/src/const_eval/error.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / const_eval / error.rs
1 use std::error::Error;
2 use std::fmt;
3
4 use rustc_errors::Diagnostic;
5 use rustc_hir as hir;
6 use rustc_middle::mir::AssertKind;
7 use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
8 use rustc_span::{Span, Symbol};
9
10 use super::InterpCx;
11 use crate::interpret::{
12 struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
13 };
14
15 /// The CTFE machine has some custom error kinds.
16 #[derive(Clone, Debug)]
17 pub enum ConstEvalErrKind {
18 NeedsRfc(String),
19 ConstAccessesStatic,
20 ModifiedGlobal,
21 AssertFailure(AssertKind<ConstInt>),
22 Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
23 Abort(String),
24 }
25
26 impl MachineStopType for ConstEvalErrKind {
27 fn is_hard_err(&self) -> bool {
28 matches!(self, Self::Panic { .. })
29 }
30 }
31
32 // The errors become `MachineStop` with plain strings when being raised.
33 // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
34 // handle these.
35 impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
36 fn into(self) -> InterpErrorInfo<'tcx> {
37 err_machine_stop!(self).into()
38 }
39 }
40
41 impl fmt::Display for ConstEvalErrKind {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 use self::ConstEvalErrKind::*;
44 match *self {
45 NeedsRfc(ref msg) => {
46 write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
47 }
48 ConstAccessesStatic => write!(f, "constant accesses static"),
49 ModifiedGlobal => {
50 write!(f, "modifying a static's initial value from another static's initializer")
51 }
52 AssertFailure(ref msg) => write!(f, "{:?}", msg),
53 Panic { msg, line, col, file } => {
54 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
55 }
56 Abort(ref msg) => write!(f, "{}", msg),
57 }
58 }
59 }
60
61 impl Error for ConstEvalErrKind {}
62
63 /// When const-evaluation errors, this type is constructed with the resulting information,
64 /// and then used to emit the error as a lint or hard error.
65 #[derive(Debug)]
66 pub struct ConstEvalErr<'tcx> {
67 pub span: Span,
68 pub error: InterpError<'tcx>,
69 pub stacktrace: Vec<FrameInfo<'tcx>>,
70 }
71
72 impl<'tcx> ConstEvalErr<'tcx> {
73 /// Turn an interpreter error into something to report to the user.
74 /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
75 /// Should be called only if the error is actually going to to be reported!
76 pub fn new<'mir, M: Machine<'mir, 'tcx>>(
77 ecx: &InterpCx<'mir, 'tcx, M>,
78 error: InterpErrorInfo<'tcx>,
79 span: Option<Span>,
80 ) -> ConstEvalErr<'tcx>
81 where
82 'tcx: 'mir,
83 {
84 error.print_backtrace();
85 let stacktrace = ecx.generate_stacktrace();
86 ConstEvalErr {
87 error: error.into_kind(),
88 stacktrace,
89 span: span.unwrap_or_else(|| ecx.cur_span()),
90 }
91 }
92
93 pub fn struct_error(
94 &self,
95 tcx: TyCtxtAt<'tcx>,
96 message: &str,
97 decorate: impl FnOnce(&mut Diagnostic),
98 ) -> ErrorHandled {
99 self.struct_generic(tcx, message, decorate, None)
100 }
101
102 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
103 self.struct_error(tcx, message, |_| {})
104 }
105
106 pub fn report_as_lint(
107 &self,
108 tcx: TyCtxtAt<'tcx>,
109 message: &str,
110 lint_root: hir::HirId,
111 span: Option<Span>,
112 ) -> ErrorHandled {
113 self.struct_generic(
114 tcx,
115 message,
116 |lint: &mut Diagnostic| {
117 // Apply the span.
118 if let Some(span) = span {
119 let primary_spans = lint.span.primary_spans().to_vec();
120 // point at the actual error as the primary span
121 lint.replace_span_with(span);
122 // point to the `const` statement as a secondary span
123 // they don't have any label
124 for sp in primary_spans {
125 if sp != span {
126 lint.span_label(sp, "");
127 }
128 }
129 }
130 },
131 Some(lint_root),
132 )
133 }
134
135 /// Create a diagnostic for this const eval error.
136 ///
137 /// Sets the message passed in via `message` and adds span labels with detailed error
138 /// information before handing control back to `decorate` to do any final annotations,
139 /// after which the diagnostic is emitted.
140 ///
141 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
142 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
143 fn struct_generic(
144 &self,
145 tcx: TyCtxtAt<'tcx>,
146 message: &str,
147 decorate: impl FnOnce(&mut Diagnostic),
148 lint_root: Option<hir::HirId>,
149 ) -> ErrorHandled {
150 let finish = |err: &mut Diagnostic, span_msg: Option<String>| {
151 trace!("reporting const eval failure at {:?}", self.span);
152 if let Some(span_msg) = span_msg {
153 err.span_label(self.span, span_msg);
154 }
155 // Add spans for the stacktrace. Don't print a single-line backtrace though.
156 if self.stacktrace.len() > 1 {
157 // Helper closure to print duplicated lines.
158 let mut flush_last_line = |last_frame, times| {
159 if let Some((line, span)) = last_frame {
160 err.span_label(span, &line);
161 // Don't print [... additional calls ...] if the number of lines is small
162 if times < 3 {
163 for _ in 0..times {
164 err.span_label(span, &line);
165 }
166 } else {
167 err.span_label(
168 span,
169 format!("[... {} additional calls {} ...]", times, &line),
170 );
171 }
172 }
173 };
174
175 let mut last_frame = None;
176 let mut times = 0;
177 for frame_info in &self.stacktrace {
178 let frame = (frame_info.to_string(), frame_info.span);
179 if last_frame.as_ref() == Some(&frame) {
180 times += 1;
181 } else {
182 flush_last_line(last_frame, times);
183 last_frame = Some(frame);
184 times = 0;
185 }
186 }
187 flush_last_line(last_frame, times);
188 }
189 // Let the caller attach any additional information it wants.
190 decorate(err);
191 };
192
193 // Special handling for certain errors
194 match &self.error {
195 // Don't emit a new diagnostic for these errors
196 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
197 return ErrorHandled::TooGeneric;
198 }
199 err_inval!(AlreadyReported(error_reported)) => {
200 return ErrorHandled::Reported(*error_reported);
201 }
202 err_inval!(Layout(LayoutError::SizeOverflow(_))) => {
203 // We must *always* hard error on these, even if the caller wants just a lint.
204 // The `message` makes little sense here, this is a more serious error than the
205 // caller thinks anyway.
206 // See <https://github.com/rust-lang/rust/pull/63152>.
207 let mut err = struct_error(tcx, &self.error.to_string());
208 finish(&mut err, None);
209 return ErrorHandled::Reported(err.emit());
210 }
211 _ => {}
212 };
213
214 let err_msg = self.error.to_string();
215
216 // Regular case - emit a lint.
217 if let Some(lint_root) = lint_root {
218 // Report as lint.
219 let hir_id =
220 self.stacktrace.iter().rev().find_map(|frame| frame.lint_root).unwrap_or(lint_root);
221 tcx.struct_span_lint_hir(
222 rustc_session::lint::builtin::CONST_ERR,
223 hir_id,
224 tcx.span,
225 |lint| {
226 let mut lint = lint.build(message);
227 finish(&mut lint, Some(err_msg));
228 lint.emit();
229 },
230 );
231 ErrorHandled::Linted
232 } else {
233 // Report as hard error.
234 let mut err = struct_error(tcx, message);
235 finish(&mut err, Some(err_msg));
236 ErrorHandled::Reported(err.emit())
237 }
238 }
239 }