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