1 use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
2
3 use crate::mir::interpret::ConstValue;
4 use crate::query::TyCtxtAt;
5 use crate::ty::{layout, tls, Ty, ValTree};
6
7 use rustc_data_structures::sync::Lock;
8 use rustc_errors::{
9 struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
10 IntoDiagnosticArg,
11 };
12 use rustc_macros::HashStable;
13 use rustc_session::CtfeBacktrace;
14 use rustc_span::def_id::DefId;
15 use rustc_target::abi::{call, Align, Size, WrappingRange};
16 use std::borrow::Cow;
17 use std::{any::Any, backtrace::Backtrace, fmt};
18
19 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
20 pub enum ErrorHandled {
21 /// Already reported an error for this evaluation, and the compilation is
22 /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
23 Reported(ReportedErrorInfo),
24 /// Don't emit an error, the evaluation failed because the MIR was generic
25 /// and the substs didn't fully monomorphize it.
26 TooGeneric,
27 }
28
29 impl From<ErrorGuaranteed> for ErrorHandled {
30 #[inline]
from(error: ErrorGuaranteed) -> ErrorHandled31 fn from(error: ErrorGuaranteed) -> ErrorHandled {
32 ErrorHandled::Reported(error.into())
33 }
34 }
35
36 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
37 pub struct ReportedErrorInfo {
38 error: ErrorGuaranteed,
39 is_tainted_by_errors: bool,
40 }
41
42 impl ReportedErrorInfo {
43 #[inline]
tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo44 pub fn tainted_by_errors(error: ErrorGuaranteed) -> ReportedErrorInfo {
45 ReportedErrorInfo { is_tainted_by_errors: true, error }
46 }
47
48 /// Returns true if evaluation failed because MIR was tainted by errors.
49 #[inline]
is_tainted_by_errors(self) -> bool50 pub fn is_tainted_by_errors(self) -> bool {
51 self.is_tainted_by_errors
52 }
53 }
54
55 impl From<ErrorGuaranteed> for ReportedErrorInfo {
56 #[inline]
from(error: ErrorGuaranteed) -> ReportedErrorInfo57 fn from(error: ErrorGuaranteed) -> ReportedErrorInfo {
58 ReportedErrorInfo { is_tainted_by_errors: false, error }
59 }
60 }
61
62 impl Into<ErrorGuaranteed> for ReportedErrorInfo {
63 #[inline]
into(self) -> ErrorGuaranteed64 fn into(self) -> ErrorGuaranteed {
65 self.error
66 }
67 }
68
69 TrivialTypeTraversalAndLiftImpls! {
70 ErrorHandled,
71 }
72
73 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
74 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
75 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
76
struct_error<'tcx>( tcx: TyCtxtAt<'tcx>, msg: &str, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>77 pub fn struct_error<'tcx>(
78 tcx: TyCtxtAt<'tcx>,
79 msg: &str,
80 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
81 struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
82 }
83
84 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
85 static_assert_size!(InterpErrorInfo<'_>, 8);
86
87 /// Packages the kind of error we got from the const code interpreter
88 /// up with a Rust-level backtrace of where the error occurred.
89 /// These should always be constructed by calling `.into()` on
90 /// an `InterpError`. In `rustc_mir::interpret`, we have `throw_err_*`
91 /// macros for this.
92 #[derive(Debug)]
93 pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
94
95 #[derive(Debug)]
96 struct InterpErrorInfoInner<'tcx> {
97 kind: InterpError<'tcx>,
98 backtrace: InterpErrorBacktrace,
99 }
100
101 #[derive(Debug)]
102 pub struct InterpErrorBacktrace {
103 backtrace: Option<Box<Backtrace>>,
104 }
105
106 impl InterpErrorBacktrace {
new() -> InterpErrorBacktrace107 pub fn new() -> InterpErrorBacktrace {
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 InterpErrorBacktrace { backtrace }
128 }
129
print_backtrace(&self)130 pub fn print_backtrace(&self) {
131 if let Some(backtrace) = self.backtrace.as_ref() {
132 print_backtrace(backtrace);
133 }
134 }
135 }
136
137 impl<'tcx> InterpErrorInfo<'tcx> {
from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self138 pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
139 Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
140 }
141
into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace)142 pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
143 let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
144 (kind, backtrace)
145 }
146
into_kind(self) -> InterpError<'tcx>147 pub fn into_kind(self) -> InterpError<'tcx> {
148 let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
149 kind
150 }
151
152 #[inline]
kind(&self) -> &InterpError<'tcx>153 pub fn kind(&self) -> &InterpError<'tcx> {
154 &self.0.kind
155 }
156 }
157
print_backtrace(backtrace: &Backtrace)158 fn print_backtrace(backtrace: &Backtrace) {
159 eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
160 }
161
162 impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
from(err: ErrorGuaranteed) -> Self163 fn from(err: ErrorGuaranteed) -> Self {
164 InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into()
165 }
166 }
167
168 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
from(kind: InterpError<'tcx>) -> Self169 fn from(kind: InterpError<'tcx>) -> Self {
170 InterpErrorInfo(Box::new(InterpErrorInfoInner {
171 kind,
172 backtrace: InterpErrorBacktrace::new(),
173 }))
174 }
175 }
176
177 /// Error information for when the program we executed turned out not to actually be a valid
178 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
179 /// where we work on generic code or execution does not have all information available.
180 #[derive(Debug)]
181 pub enum InvalidProgramInfo<'tcx> {
182 /// Resolution can fail if we are in a too generic context.
183 TooGeneric,
184 /// Abort in case errors are already reported.
185 AlreadyReported(ReportedErrorInfo),
186 /// An error occurred during layout computation.
187 Layout(layout::LayoutError<'tcx>),
188 /// An error occurred during FnAbi computation: the passed --target lacks FFI support
189 /// (which unfortunately typeck does not reject).
190 /// Not using `FnAbiError` as that contains a nested `LayoutError`.
191 FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError),
192 /// SizeOf of unsized type was requested.
193 SizeOfUnsizedType(Ty<'tcx>),
194 /// An unsized local was accessed without having been initialized.
195 /// This is not meaningful as we can't even have backing memory for such locals.
196 UninitUnsizedLocal,
197 }
198
199 /// Details of why a pointer had to be in-bounds.
200 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
201 pub enum CheckInAllocMsg {
202 /// We are dereferencing a pointer (i.e., creating a place).
203 DerefTest,
204 /// We are access memory.
205 MemoryAccessTest,
206 /// We are doing pointer arithmetic.
207 PointerArithmeticTest,
208 /// We are doing pointer offset_from.
209 OffsetFromTest,
210 /// None of the above -- generic/unspecific inbounds test.
211 InboundsTest,
212 }
213
214 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
215 pub enum InvalidMetaKind {
216 /// Size of a `[T]` is too big
217 SliceTooBig,
218 /// Size of a DST is too big
219 TooBig,
220 }
221
222 impl IntoDiagnosticArg for InvalidMetaKind {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>223 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
224 DiagnosticArgValue::Str(Cow::Borrowed(match self {
225 InvalidMetaKind::SliceTooBig => "slice_too_big",
226 InvalidMetaKind::TooBig => "too_big",
227 }))
228 }
229 }
230
231 /// Details of an access to uninitialized bytes where it is not allowed.
232 #[derive(Debug, Clone, Copy)]
233 pub struct UninitBytesAccess {
234 /// Range of the original memory access.
235 pub access: AllocRange,
236 /// Range of the uninit memory that was encountered. (Might not be maximal.)
237 pub uninit: AllocRange,
238 }
239
240 /// Information about a size mismatch.
241 #[derive(Debug)]
242 pub struct ScalarSizeMismatch {
243 pub target_size: u64,
244 pub data_size: u64,
245 }
246
247 macro_rules! impl_into_diagnostic_arg_through_debug {
248 ($($ty:ty),*$(,)?) => {$(
249 impl IntoDiagnosticArg for $ty {
250 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
251 DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
252 }
253 }
254 )*}
255 }
256
257 // These types have nice `Debug` output so we can just use them in diagnostics.
258 impl_into_diagnostic_arg_through_debug! {
259 AllocId,
260 Pointer,
261 AllocRange,
262 }
263
264 /// Error information for when the program caused Undefined Behavior.
265 #[derive(Debug)]
266 pub enum UndefinedBehaviorInfo<'a> {
267 /// Free-form case. Only for errors that are never caught! Used by miri
268 Ub(String),
269 /// Unreachable code was executed.
270 Unreachable,
271 /// A slice/array index projection went out-of-bounds.
272 BoundsCheckFailed { len: u64, index: u64 },
273 /// Something was divided by 0 (x / 0).
274 DivisionByZero,
275 /// Something was "remainded" by 0 (x % 0).
276 RemainderByZero,
277 /// Signed division overflowed (INT_MIN / -1).
278 DivisionOverflow,
279 /// Signed remainder overflowed (INT_MIN % -1).
280 RemainderOverflow,
281 /// Overflowing inbounds pointer arithmetic.
282 PointerArithOverflow,
283 /// Invalid metadata in a wide pointer
284 InvalidMeta(InvalidMetaKind),
285 /// Reading a C string that does not end within its allocation.
286 UnterminatedCString(Pointer),
287 /// Dereferencing a dangling pointer after it got freed.
288 PointerUseAfterFree(AllocId),
289 /// Used a pointer outside the bounds it is valid for.
290 /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.)
291 PointerOutOfBounds {
292 alloc_id: AllocId,
293 alloc_size: Size,
294 ptr_offset: i64,
295 ptr_size: Size,
296 msg: CheckInAllocMsg,
297 },
298 /// Using an integer as a pointer in the wrong way.
299 DanglingIntPointer(u64, CheckInAllocMsg),
300 /// Used a pointer with bad alignment.
301 AlignmentCheckFailed { required: Align, has: Align },
302 /// Writing to read-only memory.
303 WriteToReadOnly(AllocId),
304 /// Trying to access the data behind a function pointer.
305 DerefFunctionPointer(AllocId),
306 /// Trying to access the data behind a vtable pointer.
307 DerefVTablePointer(AllocId),
308 /// Using a non-boolean `u8` as bool.
309 InvalidBool(u8),
310 /// Using a non-character `u32` as character.
311 InvalidChar(u32),
312 /// The tag of an enum does not encode an actual discriminant.
313 InvalidTag(Scalar),
314 /// Using a pointer-not-to-a-function as function pointer.
315 InvalidFunctionPointer(Pointer),
316 /// Using a pointer-not-to-a-vtable as vtable pointer.
317 InvalidVTablePointer(Pointer),
318 /// Using a string that is not valid UTF-8,
319 InvalidStr(std::str::Utf8Error),
320 /// Using uninitialized data where it is not allowed.
321 InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
322 /// Working with a local that is not currently live.
323 DeadLocal,
324 /// Data size is not equal to target size.
325 ScalarSizeMismatch(ScalarSizeMismatch),
326 /// A discriminant of an uninhabited enum variant is written.
327 UninhabitedEnumVariantWritten,
328 /// Validation error.
329 Validation(ValidationErrorInfo<'a>),
330 // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
331 // dispatched
332 /// A custom (free-form) error, created by `err_ub_custom!`.
333 Custom(crate::error::CustomSubdiagnostic<'a>),
334 }
335
336 #[derive(Debug, Clone, Copy)]
337 pub enum PointerKind {
338 Ref,
339 Box,
340 }
341
342 impl IntoDiagnosticArg for PointerKind {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>343 fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
344 DiagnosticArgValue::Str(
345 match self {
346 Self::Ref => "ref",
347 Self::Box => "box",
348 }
349 .into(),
350 )
351 }
352 }
353
354 #[derive(Debug)]
355 pub struct ValidationErrorInfo<'tcx> {
356 pub path: Option<String>,
357 pub kind: ValidationErrorKind<'tcx>,
358 }
359
360 #[derive(Debug)]
361 pub enum ExpectedKind {
362 Reference,
363 Box,
364 RawPtr,
365 InitScalar,
366 Bool,
367 Char,
368 Float,
369 Int,
370 FnPtr,
371 }
372
373 impl From<PointerKind> for ExpectedKind {
from(x: PointerKind) -> ExpectedKind374 fn from(x: PointerKind) -> ExpectedKind {
375 match x {
376 PointerKind::Box => ExpectedKind::Box,
377 PointerKind::Ref => ExpectedKind::Reference,
378 }
379 }
380 }
381
382 #[derive(Debug)]
383 pub enum ValidationErrorKind<'tcx> {
384 PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
385 PtrToStatic { ptr_kind: PointerKind },
386 PtrToMut { ptr_kind: PointerKind },
387 ExpectedNonPtr { value: String },
388 MutableRefInConst,
389 NullFnPtr,
390 NeverVal,
391 NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
392 PtrOutOfRange { range: WrappingRange, max_value: u128 },
393 OutOfRange { value: String, range: WrappingRange, max_value: u128 },
394 UnsafeCell,
395 UninhabitedVal { ty: Ty<'tcx> },
396 InvalidEnumTag { value: String },
397 UninitEnumTag,
398 UninitStr,
399 Uninit { expected: ExpectedKind },
400 UninitVal,
401 InvalidVTablePtr { value: String },
402 InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
403 InvalidMetaTooLarge { ptr_kind: PointerKind },
404 UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
405 NullPtr { ptr_kind: PointerKind },
406 DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
407 DanglingPtrOutOfBounds { ptr_kind: PointerKind },
408 DanglingPtrUseAfterFree { ptr_kind: PointerKind },
409 InvalidBool { value: String },
410 InvalidChar { value: String },
411 InvalidFnPtr { value: String },
412 }
413
414 /// Error information for when the program did something that might (or might not) be correct
415 /// to do according to the Rust spec, but due to limitations in the interpreter, the
416 /// operation could not be carried out. These limitations can differ between CTFE and the
417 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
418 #[derive(Debug)]
419 pub enum UnsupportedOpInfo {
420 /// Free-form case. Only for errors that are never caught!
421 // FIXME still use translatable diagnostics
422 Unsupported(String),
423 //
424 // The variants below are only reachable from CTFE/const prop, miri will never emit them.
425 //
426 /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
427 /// cannot be represented by the CTFE interpreter.
428 PartialPointerOverwrite(Pointer<AllocId>),
429 /// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute
430 /// addresses, the resulting state cannot be represented by the CTFE interpreter.
431 PartialPointerCopy(Pointer<AllocId>),
432 /// Encountered a pointer where we needed raw bytes.
433 ReadPointerAsBytes,
434 /// Accessing thread local statics
435 ThreadLocalStatic(DefId),
436 /// Accessing an unsupported extern static.
437 ReadExternStatic(DefId),
438 }
439
440 /// Error information for when the program exhausted the resources granted to it
441 /// by the interpreter.
442 #[derive(Debug)]
443 pub enum ResourceExhaustionInfo {
444 /// The stack grew too big.
445 StackFrameLimitReached,
446 /// There is not enough memory (on the host) to perform an allocation.
447 MemoryExhausted,
448 /// The address space (of the target) is full.
449 AddressSpaceFull,
450 }
451
452 /// A trait for machine-specific errors (or other "machine stop" conditions).
453 pub trait MachineStopType: Any + fmt::Debug + Send {
454 /// The diagnostic message for this error
diagnostic_message(&self) -> DiagnosticMessage455 fn diagnostic_message(&self) -> DiagnosticMessage;
456 /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
457 /// fluent for formatting the translated diagnostic message.
add_args( self: Box<Self>, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>), )458 fn add_args(
459 self: Box<Self>,
460 adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
461 );
462 }
463
464 impl dyn MachineStopType {
465 #[inline(always)]
downcast_ref<T: Any>(&self) -> Option<&T>466 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
467 let x: &dyn Any = self;
468 x.downcast_ref()
469 }
470 }
471
472 #[derive(Debug)]
473 pub enum InterpError<'tcx> {
474 /// The program caused undefined behavior.
475 UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
476 /// The program did something the interpreter does not support (some of these *might* be UB
477 /// but the interpreter is not sure).
478 Unsupported(UnsupportedOpInfo),
479 /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
480 InvalidProgram(InvalidProgramInfo<'tcx>),
481 /// The program exhausted the interpreter's resources (stack/heap too big,
482 /// execution takes too long, ...).
483 ResourceExhaustion(ResourceExhaustionInfo),
484 /// Stop execution for a machine-controlled reason. This is never raised by
485 /// the core engine itself.
486 MachineStop(Box<dyn MachineStopType>),
487 }
488
489 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
490
491 impl InterpError<'_> {
492 /// Some errors do string formatting even if the error is never printed.
493 /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
494 /// so this method lets us detect them and `bug!` on unexpected errors.
formatted_string(&self) -> bool495 pub fn formatted_string(&self) -> bool {
496 matches!(
497 self,
498 InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
499 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
500 | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
501 )
502 }
503 }
504