]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_const_eval/src/const_eval/error.rs
New upstream version 1.65.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 UnsupportedOpInfo,
14 };
15
16 /// The CTFE machine has some custom error kinds.
17 #[derive(Clone, Debug)]
18 pub enum ConstEvalErrKind {
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 ConstAccessesStatic => write!(f, "constant accesses static"),
46 ModifiedGlobal => {
47 write!(f, "modifying a static's initial value from another static's initializer")
48 }
49 AssertFailure(ref msg) => write!(f, "{:?}", msg),
50 Panic { msg, line, col, file } => {
51 write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
52 }
53 Abort(ref msg) => write!(f, "{}", msg),
54 }
55 }
56 }
57
58 impl Error for ConstEvalErrKind {}
59
60 /// When const-evaluation errors, this type is constructed with the resulting information,
61 /// and then used to emit the error as a lint or hard error.
62 #[derive(Debug)]
63 pub struct ConstEvalErr<'tcx> {
64 pub span: Span,
65 pub error: InterpError<'tcx>,
66 pub stacktrace: Vec<FrameInfo<'tcx>>,
67 }
68
69 impl<'tcx> ConstEvalErr<'tcx> {
70 /// Turn an interpreter error into something to report to the user.
71 /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
72 /// Should be called only if the error is actually going to to be reported!
73 pub fn new<'mir, M: Machine<'mir, 'tcx>>(
74 ecx: &InterpCx<'mir, 'tcx, M>,
75 error: InterpErrorInfo<'tcx>,
76 span: Option<Span>,
77 ) -> ConstEvalErr<'tcx>
78 where
79 'tcx: 'mir,
80 {
81 error.print_backtrace();
82 let mut stacktrace = ecx.generate_stacktrace();
83 // Filter out `requires_caller_location` frames.
84 stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
85 // If `span` is missing, use topmost remaining frame, or else the "root" span from `ecx.tcx`.
86 let span = span.or_else(|| stacktrace.first().map(|f| f.span)).unwrap_or(ecx.tcx.span);
87 ConstEvalErr { error: error.into_kind(), stacktrace, span }
88 }
89
90 pub fn struct_error(
91 &self,
92 tcx: TyCtxtAt<'tcx>,
93 message: &str,
94 decorate: impl FnOnce(&mut Diagnostic),
95 ) -> ErrorHandled {
96 self.struct_generic(tcx, message, decorate, None)
97 }
98
99 pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
100 self.struct_error(tcx, message, |_| {})
101 }
102
103 pub fn report_as_lint(
104 &self,
105 tcx: TyCtxtAt<'tcx>,
106 message: &str,
107 lint_root: hir::HirId,
108 span: Option<Span>,
109 ) -> ErrorHandled {
110 self.struct_generic(
111 tcx,
112 message,
113 |lint: &mut Diagnostic| {
114 // Apply the span.
115 if let Some(span) = span {
116 let primary_spans = lint.span.primary_spans().to_vec();
117 // point at the actual error as the primary span
118 lint.replace_span_with(span);
119 // point to the `const` statement as a secondary span
120 // they don't have any label
121 for sp in primary_spans {
122 if sp != span {
123 lint.span_label(sp, "");
124 }
125 }
126 }
127 },
128 Some(lint_root),
129 )
130 }
131
132 /// Create a diagnostic for this const eval error.
133 ///
134 /// Sets the message passed in via `message` and adds span labels with detailed error
135 /// information before handing control back to `decorate` to do any final annotations,
136 /// after which the diagnostic is emitted.
137 ///
138 /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
139 /// (Except that for some errors, we ignore all that -- see `must_error` below.)
140 #[instrument(skip(self, tcx, decorate, lint_root), level = "debug")]
141 fn struct_generic(
142 &self,
143 tcx: TyCtxtAt<'tcx>,
144 message: &str,
145 decorate: impl FnOnce(&mut Diagnostic),
146 lint_root: Option<hir::HirId>,
147 ) -> ErrorHandled {
148 let finish = |err: &mut Diagnostic, span_msg: Option<String>| {
149 trace!("reporting const eval failure at {:?}", self.span);
150 if let Some(span_msg) = span_msg {
151 err.span_label(self.span, span_msg);
152 }
153 // Add some more context for select error types.
154 match self.error {
155 InterpError::Unsupported(
156 UnsupportedOpInfo::ReadPointerAsBytes
157 | UnsupportedOpInfo::PartialPointerOverwrite(_)
158 | UnsupportedOpInfo::PartialPointerCopy(_),
159 ) => {
160 err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
161 err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
162 }
163 _ => {}
164 }
165 // Add spans for the stacktrace. Don't print a single-line backtrace though.
166 if self.stacktrace.len() > 1 {
167 // Helper closure to print duplicated lines.
168 let mut flush_last_line = |last_frame, times| {
169 if let Some((line, span)) = last_frame {
170 err.span_label(span, &line);
171 // Don't print [... additional calls ...] if the number of lines is small
172 if times < 3 {
173 for _ in 0..times {
174 err.span_label(span, &line);
175 }
176 } else {
177 err.span_label(
178 span,
179 format!("[... {} additional calls {} ...]", times, &line),
180 );
181 }
182 }
183 };
184
185 let mut last_frame = None;
186 let mut times = 0;
187 for frame_info in &self.stacktrace {
188 let frame = (frame_info.to_string(), frame_info.span);
189 if last_frame.as_ref() == Some(&frame) {
190 times += 1;
191 } else {
192 flush_last_line(last_frame, times);
193 last_frame = Some(frame);
194 times = 0;
195 }
196 }
197 flush_last_line(last_frame, times);
198 }
199 // Let the caller attach any additional information it wants.
200 decorate(err);
201 };
202
203 debug!("self.error: {:?}", self.error);
204 // Special handling for certain errors
205 match &self.error {
206 // Don't emit a new diagnostic for these errors
207 err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
208 return ErrorHandled::TooGeneric;
209 }
210 err_inval!(AlreadyReported(error_reported)) => {
211 return ErrorHandled::Reported(*error_reported);
212 }
213 err_inval!(Layout(LayoutError::SizeOverflow(_))) => {
214 // We must *always* hard error on these, even if the caller wants just a lint.
215 // The `message` makes little sense here, this is a more serious error than the
216 // caller thinks anyway.
217 // See <https://github.com/rust-lang/rust/pull/63152>.
218 let mut err = struct_error(tcx, &self.error.to_string());
219 finish(&mut err, None);
220 return ErrorHandled::Reported(err.emit());
221 }
222 _ => {}
223 };
224
225 let err_msg = self.error.to_string();
226
227 // Regular case - emit a lint.
228 if let Some(lint_root) = lint_root {
229 // Report as lint.
230 let hir_id =
231 self.stacktrace.iter().rev().find_map(|frame| frame.lint_root).unwrap_or(lint_root);
232 tcx.struct_span_lint_hir(
233 rustc_session::lint::builtin::CONST_ERR,
234 hir_id,
235 tcx.span,
236 |lint| {
237 let mut lint = lint.build(message);
238 finish(&mut lint, Some(err_msg));
239 lint.emit();
240 },
241 );
242 ErrorHandled::Linted
243 } else {
244 // Report as hard error.
245 let mut err = struct_error(tcx, message);
246 finish(&mut err, Some(err_msg));
247 ErrorHandled::Reported(err.emit())
248 }
249 }
250 }