]>
Commit | Line | Data |
---|---|---|
1b1a35ee | 1 | use super::{AllocId, ConstAlloc, Pointer, Scalar}; |
ea8adc8c | 2 | |
74b04a01 | 3 | use crate::mir::interpret::ConstValue; |
923072b8 | 4 | use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; |
ea8adc8c | 5 | |
ba9703b0 | 6 | use rustc_data_structures::sync::Lock; |
5e7ed085 | 7 | use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed}; |
e1599b0c | 8 | use rustc_macros::HashStable; |
ba9703b0 | 9 | use rustc_session::CtfeBacktrace; |
3dfed10e | 10 | use rustc_span::def_id::DefId; |
a2a8927a | 11 | use rustc_target::abi::{call, Align, Size}; |
6a06907d | 12 | use std::{any::Any, backtrace::Backtrace, fmt}; |
e1599b0c | 13 | |
3dfed10e | 14 | #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] |
a1dfa0c6 | 15 | pub enum ErrorHandled { |
ba9703b0 XL |
16 | /// Already reported an error for this evaluation, and the compilation is |
17 | /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. | |
5e7ed085 | 18 | Reported(ErrorGuaranteed), |
ba9703b0 XL |
19 | /// Already emitted a lint for this evaluation. |
20 | Linted, | |
a1dfa0c6 XL |
21 | /// Don't emit an error, the evaluation failed because the MIR was generic |
22 | /// and the substs didn't fully monomorphize it. | |
23 | TooGeneric, | |
24 | } | |
25 | ||
5e7ed085 FG |
26 | impl From<ErrorGuaranteed> for ErrorHandled { |
27 | fn from(err: ErrorGuaranteed) -> ErrorHandled { | |
1b1a35ee XL |
28 | ErrorHandled::Reported(err) |
29 | } | |
30 | } | |
31 | ||
fc512014 | 32 | TrivialTypeFoldableAndLiftImpls! { |
dc9dc135 XL |
33 | ErrorHandled, |
34 | } | |
35 | ||
1b1a35ee XL |
36 | pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; |
37 | pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; | |
923072b8 | 38 | pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>; |
8faf50e0 | 39 | |
5e7ed085 FG |
40 | pub fn struct_error<'tcx>( |
41 | tcx: TyCtxtAt<'tcx>, | |
42 | msg: &str, | |
43 | ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { | |
8faf50e0 XL |
44 | struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) |
45 | } | |
46 | ||
6a06907d XL |
47 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
48 | static_assert_size!(InterpErrorInfo<'_>, 8); | |
49 | ||
dc9dc135 | 50 | /// Packages the kind of error we got from the const code interpreter |
60c5eb7d | 51 | /// up with a Rust-level backtrace of where the error occurred. |
cdc7bbd5 | 52 | /// These should always be constructed by calling `.into()` on |
94222f64 | 53 | /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*` |
416331ca | 54 | /// macros for this. |
60c5eb7d | 55 | #[derive(Debug)] |
6a06907d XL |
56 | pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>); |
57 | ||
58 | #[derive(Debug)] | |
59 | struct InterpErrorInfoInner<'tcx> { | |
60 | kind: InterpError<'tcx>, | |
dc9dc135 | 61 | backtrace: Option<Box<Backtrace>>, |
a1dfa0c6 XL |
62 | } |
63 | ||
416331ca XL |
64 | impl fmt::Display for InterpErrorInfo<'_> { |
65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
6a06907d | 66 | write!(f, "{}", self.0.kind) |
416331ca XL |
67 | } |
68 | } | |
69 | ||
a2a8927a | 70 | impl<'tcx> InterpErrorInfo<'tcx> { |
f9f354fc | 71 | pub fn print_backtrace(&self) { |
6a06907d | 72 | if let Some(backtrace) = self.0.backtrace.as_ref() { |
f9f354fc | 73 | print_backtrace(backtrace); |
a1dfa0c6 XL |
74 | } |
75 | } | |
6a06907d XL |
76 | |
77 | pub fn into_kind(self) -> InterpError<'tcx> { | |
78 | let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self; | |
79 | kind | |
80 | } | |
81 | ||
82 | #[inline] | |
83 | pub fn kind(&self) -> &InterpError<'tcx> { | |
84 | &self.0.kind | |
85 | } | |
a1dfa0c6 XL |
86 | } |
87 | ||
f9f354fc XL |
88 | fn print_backtrace(backtrace: &Backtrace) { |
89 | eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); | |
ea8adc8c XL |
90 | } |
91 | ||
74b04a01 | 92 | impl From<ErrorHandled> for InterpErrorInfo<'_> { |
e1599b0c XL |
93 | fn from(err: ErrorHandled) -> Self { |
94 | match err { | |
5e7ed085 | 95 | ErrorHandled::Reported(ErrorGuaranteed { .. }) | ErrorHandled::Linted => { |
ba9703b0 XL |
96 | err_inval!(ReferencedConstant) |
97 | } | |
e1599b0c | 98 | ErrorHandled::TooGeneric => err_inval!(TooGeneric), |
dfeec247 XL |
99 | } |
100 | .into() | |
e1599b0c XL |
101 | } |
102 | } | |
103 | ||
5e7ed085 FG |
104 | impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { |
105 | fn from(err: ErrorGuaranteed) -> Self { | |
29967ef6 XL |
106 | InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into() |
107 | } | |
108 | } | |
109 | ||
416331ca XL |
110 | impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> { |
111 | fn from(kind: InterpError<'tcx>) -> Self { | |
3dfed10e XL |
112 | let capture_backtrace = tls::with_opt(|tcx| { |
113 | if let Some(tcx) = tcx { | |
114 | *Lock::borrow(&tcx.sess.ctfe_backtrace) | |
ba9703b0 XL |
115 | } else { |
116 | CtfeBacktrace::Disabled | |
117 | } | |
118 | }); | |
a1dfa0c6 | 119 | |
ba9703b0 XL |
120 | let backtrace = match capture_backtrace { |
121 | CtfeBacktrace::Disabled => None, | |
f9f354fc | 122 | CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), |
ba9703b0 XL |
123 | CtfeBacktrace::Immediate => { |
124 | // Print it now. | |
f9f354fc XL |
125 | let backtrace = Backtrace::force_capture(); |
126 | print_backtrace(&backtrace); | |
ba9703b0 | 127 | None |
dfeec247 | 128 | } |
a1dfa0c6 | 129 | }; |
ba9703b0 | 130 | |
6a06907d | 131 | InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace })) |
ea8adc8c XL |
132 | } |
133 | } | |
134 | ||
e1599b0c XL |
135 | /// Error information for when the program we executed turned out not to actually be a valid |
136 | /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp | |
137 | /// where we work on generic code or execution does not have all information available. | |
416331ca XL |
138 | pub enum InvalidProgramInfo<'tcx> { |
139 | /// Resolution can fail if we are in a too generic context. | |
140 | TooGeneric, | |
141 | /// Cannot compute this constant because it depends on another one | |
142 | /// which already produced an error. | |
143 | ReferencedConstant, | |
29967ef6 | 144 | /// Abort in case errors are already reported. |
5e7ed085 | 145 | AlreadyReported(ErrorGuaranteed), |
416331ca XL |
146 | /// An error occurred during layout computation. |
147 | Layout(layout::LayoutError<'tcx>), | |
a2a8927a XL |
148 | /// An error occurred during FnAbi computation: the passed --target lacks FFI support |
149 | /// (which unfortunately typeck does not reject). | |
150 | /// Not using `FnAbiError` as that contains a nested `LayoutError`. | |
151 | FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), | |
5869c6ff XL |
152 | /// SizeOf of unsized type was requested. |
153 | SizeOfUnsizedType(Ty<'tcx>), | |
416331ca | 154 | } |
b7449926 | 155 | |
f9f354fc | 156 | impl fmt::Display for InvalidProgramInfo<'_> { |
416331ca XL |
157 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
158 | use InvalidProgramInfo::*; | |
159 | match self { | |
dfeec247 XL |
160 | TooGeneric => write!(f, "encountered overly generic constant"), |
161 | ReferencedConstant => write!(f, "referenced constant has errors"), | |
5e7ed085 | 162 | AlreadyReported(ErrorGuaranteed { .. }) => { |
ba9703b0 XL |
163 | write!(f, "encountered constants with type errors, stopping evaluation") |
164 | } | |
dfeec247 | 165 | Layout(ref err) => write!(f, "{}", err), |
a2a8927a | 166 | FnAbiAdjustForForeignAbi(ref err) => write!(f, "{}", err), |
5869c6ff | 167 | SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty), |
416331ca XL |
168 | } |
169 | } | |
170 | } | |
171 | ||
f9f354fc | 172 | /// Details of why a pointer had to be in-bounds. |
3dfed10e | 173 | #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] |
f9f354fc | 174 | pub enum CheckInAllocMsg { |
136023e0 XL |
175 | /// We are dereferencing a pointer (i.e., creating a place). |
176 | DerefTest, | |
17df50a5 | 177 | /// We are access memory. |
f9f354fc | 178 | MemoryAccessTest, |
17df50a5 | 179 | /// We are doing pointer arithmetic. |
f9f354fc | 180 | PointerArithmeticTest, |
5e7ed085 FG |
181 | /// We are doing pointer offset_from. |
182 | OffsetFromTest, | |
17df50a5 | 183 | /// None of the above -- generic/unspecific inbounds test. |
f9f354fc XL |
184 | InboundsTest, |
185 | } | |
186 | ||
187 | impl fmt::Display for CheckInAllocMsg { | |
136023e0 XL |
188 | /// When this is printed as an error the context looks like this: |
189 | /// "{msg}0x01 is not a valid pointer". | |
f9f354fc XL |
190 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
191 | write!( | |
192 | f, | |
193 | "{}", | |
194 | match *self { | |
136023e0 | 195 | CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ", |
17df50a5 XL |
196 | CheckInAllocMsg::MemoryAccessTest => "memory access failed: ", |
197 | CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic failed: ", | |
5e7ed085 | 198 | CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ", |
17df50a5 | 199 | CheckInAllocMsg::InboundsTest => "", |
f9f354fc XL |
200 | } |
201 | ) | |
202 | } | |
203 | } | |
204 | ||
205 | /// Details of an access to uninitialized bytes where it is not allowed. | |
206 | #[derive(Debug)] | |
207 | pub struct UninitBytesAccess { | |
208 | /// Location of the original memory access. | |
17df50a5 | 209 | pub access_offset: Size, |
f9f354fc XL |
210 | /// Size of the original memory access. |
211 | pub access_size: Size, | |
212 | /// Location of the first uninitialized byte that was accessed. | |
17df50a5 | 213 | pub uninit_offset: Size, |
f9f354fc XL |
214 | /// Number of consecutive uninitialized bytes that were accessed. |
215 | pub uninit_size: Size, | |
216 | } | |
217 | ||
04454e1e FG |
218 | /// Information about a size mismatch. |
219 | #[derive(Debug)] | |
220 | pub struct ScalarSizeMismatch { | |
221 | pub target_size: u64, | |
222 | pub data_size: u64, | |
223 | } | |
224 | ||
e1599b0c | 225 | /// Error information for when the program caused Undefined Behavior. |
f9f354fc | 226 | pub enum UndefinedBehaviorInfo<'tcx> { |
416331ca XL |
227 | /// Free-form case. Only for errors that are never caught! |
228 | Ub(String), | |
416331ca XL |
229 | /// Unreachable code was executed. |
230 | Unreachable, | |
60c5eb7d | 231 | /// A slice/array index projection went out-of-bounds. |
ba9703b0 XL |
232 | BoundsCheckFailed { |
233 | len: u64, | |
234 | index: u64, | |
235 | }, | |
60c5eb7d XL |
236 | /// Something was divided by 0 (x / 0). |
237 | DivisionByZero, | |
238 | /// Something was "remainded" by 0 (x % 0). | |
239 | RemainderByZero, | |
5e7ed085 FG |
240 | /// Signed division overflowed (INT_MIN / -1). |
241 | DivisionOverflow, | |
242 | /// Signed remainder overflowed (INT_MIN % -1). | |
243 | RemainderOverflow, | |
60c5eb7d XL |
244 | /// Overflowing inbounds pointer arithmetic. |
245 | PointerArithOverflow, | |
74b04a01 XL |
246 | /// Invalid metadata in a wide pointer (using `str` to avoid allocations). |
247 | InvalidMeta(&'static str), | |
f9f354fc | 248 | /// Invalid drop function in vtable. |
136023e0 XL |
249 | InvalidVtableDropFn(FnSig<'tcx>), |
250 | /// Invalid size in a vtable: too large. | |
251 | InvalidVtableSize, | |
252 | /// Invalid alignment in a vtable: too large, or not a power of 2. | |
253 | InvalidVtableAlignment(String), | |
ba9703b0 XL |
254 | /// Reading a C string that does not end within its allocation. |
255 | UnterminatedCString(Pointer), | |
256 | /// Dereferencing a dangling pointer after it got freed. | |
257 | PointerUseAfterFree(AllocId), | |
258 | /// Used a pointer outside the bounds it is valid for. | |
136023e0 | 259 | /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.) |
ba9703b0 | 260 | PointerOutOfBounds { |
136023e0 XL |
261 | alloc_id: AllocId, |
262 | alloc_size: Size, | |
263 | ptr_offset: i64, | |
264 | ptr_size: Size, | |
ba9703b0 | 265 | msg: CheckInAllocMsg, |
ba9703b0 | 266 | }, |
f9f354fc XL |
267 | /// Using an integer as a pointer in the wrong way. |
268 | DanglingIntPointer(u64, CheckInAllocMsg), | |
ba9703b0 XL |
269 | /// Used a pointer with bad alignment. |
270 | AlignmentCheckFailed { | |
271 | required: Align, | |
272 | has: Align, | |
273 | }, | |
ba9703b0 XL |
274 | /// Writing to read-only memory. |
275 | WriteToReadOnly(AllocId), | |
ba9703b0 XL |
276 | // Trying to access the data behind a function pointer. |
277 | DerefFunctionPointer(AllocId), | |
278 | /// The value validity check found a problem. | |
279 | /// Should only be thrown by `validity.rs` and always point out which part of the value | |
280 | /// is the problem. | |
136023e0 XL |
281 | ValidationFailure { |
282 | /// The "path" to the value in question, e.g. `.0[5].field` for a struct | |
283 | /// field in the 6th element of an array that is the first element of a tuple. | |
284 | path: Option<String>, | |
285 | msg: String, | |
286 | }, | |
ba9703b0 XL |
287 | /// Using a non-boolean `u8` as bool. |
288 | InvalidBool(u8), | |
289 | /// Using a non-character `u32` as character. | |
290 | InvalidChar(u32), | |
f035d41b XL |
291 | /// The tag of an enum does not encode an actual discriminant. |
292 | InvalidTag(Scalar), | |
f9f354fc XL |
293 | /// Using a pointer-not-to-a-function as function pointer. |
294 | InvalidFunctionPointer(Pointer), | |
295 | /// Using a string that is not valid UTF-8, | |
296 | InvalidStr(std::str::Utf8Error), | |
ba9703b0 | 297 | /// Using uninitialized data where it is not allowed. |
17df50a5 | 298 | InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>), |
ba9703b0 XL |
299 | /// Working with a local that is not currently live. |
300 | DeadLocal, | |
f9f354fc | 301 | /// Data size is not equal to target size. |
04454e1e | 302 | ScalarSizeMismatch(ScalarSizeMismatch), |
c295e0f8 XL |
303 | /// A discriminant of an uninhabited enum variant is written. |
304 | UninhabitedEnumVariantWritten, | |
416331ca | 305 | } |
48663c56 | 306 | |
f9f354fc | 307 | impl fmt::Display for UndefinedBehaviorInfo<'_> { |
416331ca XL |
308 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
309 | use UndefinedBehaviorInfo::*; | |
310 | match self { | |
ba9703b0 | 311 | Ub(msg) => write!(f, "{}", msg), |
dfeec247 | 312 | Unreachable => write!(f, "entering unreachable code"), |
f9f354fc XL |
313 | BoundsCheckFailed { ref len, ref index } => { |
314 | write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) | |
315 | } | |
dfeec247 XL |
316 | DivisionByZero => write!(f, "dividing by zero"), |
317 | RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), | |
5e7ed085 FG |
318 | DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"), |
319 | RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), | |
dfeec247 | 320 | PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), |
74b04a01 | 321 | InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), |
136023e0 | 322 | InvalidVtableDropFn(sig) => write!( |
f9f354fc XL |
323 | f, |
324 | "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", | |
325 | sig | |
326 | ), | |
136023e0 XL |
327 | InvalidVtableSize => { |
328 | write!(f, "invalid vtable: size is bigger than largest supported object") | |
329 | } | |
330 | InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg), | |
ba9703b0 XL |
331 | UnterminatedCString(p) => write!( |
332 | f, | |
136023e0 | 333 | "reading a null-terminated string starting at {:?} with no null found before end of allocation", |
ba9703b0 XL |
334 | p, |
335 | ), | |
336 | PointerUseAfterFree(a) => { | |
f9f354fc | 337 | write!(f, "pointer to {} was dereferenced after this allocation got freed", a) |
ba9703b0 | 338 | } |
136023e0 XL |
339 | PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => { |
340 | write!( | |
341 | f, | |
342 | "{}{alloc_id} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds", | |
343 | msg, | |
344 | alloc_id = alloc_id, | |
345 | alloc_size = alloc_size.bytes(), | |
346 | ptr_offset = ptr_offset, | |
347 | ) | |
348 | } | |
349 | PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!( | |
ba9703b0 | 350 | f, |
136023e0 | 351 | "{}{alloc_id} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds", |
ba9703b0 | 352 | msg, |
136023e0 XL |
353 | alloc_id = alloc_id, |
354 | alloc_size = alloc_size.bytes(), | |
355 | ptr_size = ptr_size.bytes(), | |
356 | ptr_size_p = pluralize!(ptr_size.bytes()), | |
357 | ptr_offset = ptr_offset, | |
ba9703b0 | 358 | ), |
17df50a5 XL |
359 | DanglingIntPointer(0, CheckInAllocMsg::InboundsTest) => { |
360 | write!(f, "null pointer is not a valid pointer for this operation") | |
f9f354fc | 361 | } |
5e7ed085 FG |
362 | DanglingIntPointer(0, msg) => { |
363 | write!(f, "{}null pointer is not a valid pointer", msg) | |
364 | } | |
f9f354fc | 365 | DanglingIntPointer(i, msg) => { |
17df50a5 | 366 | write!(f, "{}0x{:x} is not a valid pointer", msg, i) |
f9f354fc | 367 | } |
ba9703b0 XL |
368 | AlignmentCheckFailed { required, has } => write!( |
369 | f, | |
370 | "accessing memory with alignment {}, but alignment {} is required", | |
371 | has.bytes(), | |
372 | required.bytes() | |
373 | ), | |
f9f354fc XL |
374 | WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), |
375 | DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), | |
136023e0 XL |
376 | ValidationFailure { path: None, msg } => write!(f, "type validation failed: {}", msg), |
377 | ValidationFailure { path: Some(path), msg } => { | |
378 | write!(f, "type validation failed at {}: {}", path, msg) | |
379 | } | |
f9f354fc XL |
380 | InvalidBool(b) => { |
381 | write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) | |
382 | } | |
383 | InvalidChar(c) => { | |
384 | write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) | |
385 | } | |
5e7ed085 | 386 | InvalidTag(val) => write!(f, "enum value has invalid tag: {:x}", val), |
ba9703b0 | 387 | InvalidFunctionPointer(p) => { |
136023e0 | 388 | write!(f, "using {:?} as function pointer but it does not point to a function", p) |
ba9703b0 | 389 | } |
f9f354fc | 390 | InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), |
17df50a5 | 391 | InvalidUninitBytes(Some((alloc, access))) => write!( |
ba9703b0 | 392 | f, |
136023e0 XL |
393 | "reading {} byte{} of memory starting at {:?}, \ |
394 | but {} byte{} {} uninitialized starting at {:?}, \ | |
f9f354fc XL |
395 | and this operation requires initialized memory", |
396 | access.access_size.bytes(), | |
397 | pluralize!(access.access_size.bytes()), | |
17df50a5 | 398 | Pointer::new(*alloc, access.access_offset), |
f9f354fc XL |
399 | access.uninit_size.bytes(), |
400 | pluralize!(access.uninit_size.bytes()), | |
5e7ed085 | 401 | pluralize!("is", access.uninit_size.bytes()), |
17df50a5 | 402 | Pointer::new(*alloc, access.uninit_offset), |
ba9703b0 | 403 | ), |
f9f354fc | 404 | InvalidUninitBytes(None) => write!( |
ba9703b0 XL |
405 | f, |
406 | "using uninitialized data, but this operation requires initialized memory" | |
407 | ), | |
408 | DeadLocal => write!(f, "accessing a dead local variable"), | |
04454e1e | 409 | ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!( |
f9f354fc XL |
410 | f, |
411 | "scalar size mismatch: expected {} bytes but got {} bytes instead", | |
412 | target_size, data_size | |
413 | ), | |
c295e0f8 XL |
414 | UninhabitedEnumVariantWritten => { |
415 | write!(f, "writing discriminant of an uninhabited enum") | |
416 | } | |
416331ca XL |
417 | } |
418 | } | |
419 | } | |
420 | ||
e1599b0c XL |
421 | /// Error information for when the program did something that might (or might not) be correct |
422 | /// to do according to the Rust spec, but due to limitations in the interpreter, the | |
423 | /// operation could not be carried out. These limitations can differ between CTFE and the | |
f9f354fc | 424 | /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. |
ba9703b0 | 425 | pub enum UnsupportedOpInfo { |
416331ca XL |
426 | /// Free-form case. Only for errors that are never caught! |
427 | Unsupported(String), | |
ba9703b0 | 428 | /// Encountered a pointer where we needed raw bytes. |
ea8adc8c | 429 | ReadPointerAsBytes, |
94222f64 | 430 | /// Overwriting parts of a pointer; the resulting state cannot be represented in our |
923072b8 | 431 | /// `Allocation` data structure. See <https://github.com/rust-lang/miri/issues/2181>. |
94222f64 | 432 | PartialPointerOverwrite(Pointer<AllocId>), |
f9f354fc XL |
433 | // |
434 | // The variants below are only reachable from CTFE/const prop, miri will never emit them. | |
435 | // | |
f9f354fc XL |
436 | /// Accessing thread local statics |
437 | ThreadLocalStatic(DefId), | |
3dfed10e XL |
438 | /// Accessing an unsupported extern static. |
439 | ReadExternStatic(DefId), | |
ea8adc8c XL |
440 | } |
441 | ||
f9f354fc | 442 | impl fmt::Display for UnsupportedOpInfo { |
a1dfa0c6 | 443 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
416331ca XL |
444 | use UnsupportedOpInfo::*; |
445 | match self { | |
dfeec247 | 446 | Unsupported(ref msg) => write!(f, "{}", msg), |
94222f64 XL |
447 | ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"), |
448 | PartialPointerOverwrite(ptr) => { | |
449 | write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr) | |
450 | } | |
f9f354fc | 451 | ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), |
94222f64 | 452 | ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did), |
416331ca XL |
453 | } |
454 | } | |
455 | } | |
456 | ||
e1599b0c XL |
457 | /// Error information for when the program exhausted the resources granted to it |
458 | /// by the interpreter. | |
416331ca XL |
459 | pub enum ResourceExhaustionInfo { |
460 | /// The stack grew too big. | |
461 | StackFrameLimitReached, | |
ba9703b0 XL |
462 | /// The program ran for too long. |
463 | /// | |
464 | /// The exact limit is set by the `const_eval_limit` attribute. | |
465 | StepLimitReached, | |
136023e0 XL |
466 | /// There is not enough memory to perform an allocation. |
467 | MemoryExhausted, | |
416331ca XL |
468 | } |
469 | ||
f9f354fc | 470 | impl fmt::Display for ResourceExhaustionInfo { |
416331ca XL |
471 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
472 | use ResourceExhaustionInfo::*; | |
473 | match self { | |
dfeec247 XL |
474 | StackFrameLimitReached => { |
475 | write!(f, "reached the configured maximum number of stack frames") | |
476 | } | |
ba9703b0 XL |
477 | StepLimitReached => { |
478 | write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") | |
479 | } | |
136023e0 XL |
480 | MemoryExhausted => { |
481 | write!(f, "tried to allocate more memory than available to compiler") | |
482 | } | |
416331ca XL |
483 | } |
484 | } | |
485 | } | |
486 | ||
ba9703b0 XL |
487 | /// A trait to work around not having trait object upcasting. |
488 | pub trait AsAny: Any { | |
489 | fn as_any(&self) -> &dyn Any; | |
490 | } | |
ba9703b0 XL |
491 | impl<T: Any> AsAny for T { |
492 | #[inline(always)] | |
493 | fn as_any(&self) -> &dyn Any { | |
494 | self | |
495 | } | |
496 | } | |
497 | ||
498 | /// A trait for machine-specific errors (or other "machine stop" conditions). | |
17df50a5 XL |
499 | pub trait MachineStopType: AsAny + fmt::Display + Send { |
500 | /// If `true`, emit a hard error instead of going through the `CONST_ERR` lint | |
501 | fn is_hard_err(&self) -> bool { | |
502 | false | |
503 | } | |
504 | } | |
ba9703b0 XL |
505 | |
506 | impl dyn MachineStopType { | |
507 | #[inline(always)] | |
508 | pub fn downcast_ref<T: Any>(&self) -> Option<&T> { | |
509 | self.as_any().downcast_ref() | |
510 | } | |
511 | } | |
512 | ||
416331ca | 513 | pub enum InterpError<'tcx> { |
416331ca | 514 | /// The program caused undefined behavior. |
f9f354fc | 515 | UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), |
416331ca XL |
516 | /// The program did something the interpreter does not support (some of these *might* be UB |
517 | /// but the interpreter is not sure). | |
ba9703b0 | 518 | Unsupported(UnsupportedOpInfo), |
60c5eb7d | 519 | /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). |
416331ca XL |
520 | InvalidProgram(InvalidProgramInfo<'tcx>), |
521 | /// The program exhausted the interpreter's resources (stack/heap too big, | |
60c5eb7d | 522 | /// execution takes too long, ...). |
416331ca | 523 | ResourceExhaustion(ResourceExhaustionInfo), |
60c5eb7d XL |
524 | /// Stop execution for a machine-controlled reason. This is never raised by |
525 | /// the core engine itself. | |
ba9703b0 | 526 | MachineStop(Box<dyn MachineStopType>), |
416331ca XL |
527 | } |
528 | ||
529 | pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>; | |
530 | ||
531 | impl fmt::Display for InterpError<'_> { | |
532 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f9f354fc XL |
533 | use InterpError::*; |
534 | match *self { | |
535 | Unsupported(ref msg) => write!(f, "{}", msg), | |
536 | InvalidProgram(ref msg) => write!(f, "{}", msg), | |
537 | UndefinedBehavior(ref msg) => write!(f, "{}", msg), | |
538 | ResourceExhaustion(ref msg) => write!(f, "{}", msg), | |
539 | MachineStop(ref msg) => write!(f, "{}", msg), | |
540 | } | |
416331ca XL |
541 | } |
542 | } | |
543 | ||
f9f354fc | 544 | // Forward `Debug` to `Display`, so it does not look awful. |
416331ca XL |
545 | impl fmt::Debug for InterpError<'_> { |
546 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
f9f354fc | 547 | fmt::Display::fmt(self, f) |
74b04a01 XL |
548 | } |
549 | } | |
550 | ||
551 | impl InterpError<'_> { | |
136023e0 | 552 | /// Some errors do string formatting even if the error is never printed. |
6a06907d XL |
553 | /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, |
554 | /// so this method lets us detect them and `bug!` on unexpected errors. | |
555 | pub fn formatted_string(&self) -> bool { | |
3c0e092e XL |
556 | matches!( |
557 | self, | |
ba9703b0 | 558 | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) |
3c0e092e XL |
559 | | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. }) |
560 | | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) | |
561 | ) | |
ea8adc8c | 562 | } |
136023e0 XL |
563 | |
564 | /// Should this error be reported as a hard error, preventing compilation, or a soft error, | |
565 | /// causing a deny-by-default lint? | |
566 | pub fn is_hard_err(&self) -> bool { | |
567 | use InterpError::*; | |
568 | match *self { | |
569 | MachineStop(ref err) => err.is_hard_err(), | |
570 | UndefinedBehavior(_) => true, | |
571 | ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) => true, | |
572 | _ => false, | |
573 | } | |
574 | } | |
ea8adc8c | 575 | } |