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