4 use rustc_errors
::Diagnostic
;
6 use rustc_middle
::mir
::AssertKind
;
7 use rustc_middle
::ty
::{layout::LayoutError, query::TyCtxtAt, ConstInt}
;
8 use rustc_span
::{Span, Symbol}
;
11 use crate::interpret
::{
12 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
{
21 AssertFailure(AssertKind
<ConstInt
>),
22 Panic { msg: Symbol, line: u32, col: u32, file: Symbol }
,
26 impl MachineStopType
for ConstEvalErrKind
{
27 fn is_hard_err(&self) -> bool
{
28 matches
!(self, Self::Panic { .. }
)
32 // The errors become `MachineStop` with plain strings when being raised.
33 // `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
35 impl<'tcx
> Into
<InterpErrorInfo
<'tcx
>> for ConstEvalErrKind
{
36 fn into(self) -> InterpErrorInfo
<'tcx
> {
37 err_machine_stop
!(self).into()
41 impl fmt
::Display
for ConstEvalErrKind
{
42 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
43 use self::ConstEvalErrKind
::*;
45 NeedsRfc(ref msg
) => {
46 write
!(f
, "\"{}\" needs an rfc before being allowed inside constants", msg
)
48 ConstAccessesStatic
=> write
!(f
, "constant accesses static"),
50 write
!(f
, "modifying a static's initial value from another static's initializer")
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
)
56 Abort(ref msg
) => write
!(f
, "{}", msg
),
61 impl Error
for ConstEvalErrKind {}
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.
66 pub struct ConstEvalErr
<'tcx
> {
68 pub error
: InterpError
<'tcx
>,
69 pub stacktrace
: Vec
<FrameInfo
<'tcx
>>,
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
>,
80 ) -> ConstEvalErr
<'tcx
>
84 error
.print_backtrace();
85 let stacktrace
= ecx
.generate_stacktrace();
87 error
: error
.into_kind(),
89 span
: span
.unwrap_or_else(|| ecx
.cur_span()),
97 decorate
: impl FnOnce(&mut Diagnostic
),
99 self.struct_generic(tcx
, message
, decorate
, None
)
102 pub fn report_as_error(&self, tcx
: TyCtxtAt
<'tcx
>, message
: &str) -> ErrorHandled
{
103 self.struct_error(tcx
, message
, |_
| {}
)
106 pub fn report_as_lint(
110 lint_root
: hir
::HirId
,
116 |lint
: &mut Diagnostic
| {
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
{
126 lint
.span_label(sp
, "");
135 /// Create a diagnostic for this const eval error.
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.
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.)
147 decorate
: impl FnOnce(&mut Diagnostic
),
148 lint_root
: Option
<hir
::HirId
>,
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
);
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
164 err
.span_label(span
, &line
);
169 format
!("[... {} additional calls {} ...]", times
, &line
),
175 let mut last_frame
= None
;
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
) {
182 flush_last_line(last_frame
, times
);
183 last_frame
= Some(frame
);
187 flush_last_line(last_frame
, times
);
189 // Let the caller attach any additional information it wants.
193 // Special handling for certain errors
195 // Don't emit a new diagnostic for these errors
196 err_inval
!(Layout(LayoutError
::Unknown(_
))) | err_inval
!(TooGeneric
) => {
197 return ErrorHandled
::TooGeneric
;
199 err_inval
!(AlreadyReported(error_reported
)) => {
200 return ErrorHandled
::Reported(*error_reported
);
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());
214 let err_msg
= self.error
.to_string();
216 // Regular case - emit a lint.
217 if let Some(lint_root
) = lint_root
{
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
,
226 let mut lint
= lint
.build(message
);
227 finish(&mut lint
, Some(err_msg
));
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())