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