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