]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/mir/interpret/error.rs
review
[rust.git] / compiler / rustc_middle / src / mir / interpret / error.rs
1 use super::{AllocId, Pointer, RawConst, 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::{Align, Size};
12 use std::{any::Any, backtrace::Backtrace, fmt, mem};
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 CloneTypeFoldableAndLiftImpls! {
33     ErrorHandled,
34 }
35
36 pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
37 pub type ConstEvalResult<'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 /// Packages the kind of error we got from the const code interpreter
44 /// up with a Rust-level backtrace of where the error occurred.
45 /// Thsese should always be constructed by calling `.into()` on
46 /// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*`
47 /// macros for this.
48 #[derive(Debug)]
49 pub struct InterpErrorInfo<'tcx> {
50     pub kind: InterpError<'tcx>,
51     backtrace: Option<Box<Backtrace>>,
52 }
53
54 impl fmt::Display for InterpErrorInfo<'_> {
55     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56         write!(f, "{}", self.kind)
57     }
58 }
59
60 impl InterpErrorInfo<'_> {
61     pub fn print_backtrace(&self) {
62         if let Some(backtrace) = self.backtrace.as_ref() {
63             print_backtrace(backtrace);
64         }
65     }
66 }
67
68 fn print_backtrace(backtrace: &Backtrace) {
69     eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
70 }
71
72 impl From<ErrorHandled> for InterpErrorInfo<'_> {
73     fn from(err: ErrorHandled) -> Self {
74         match err {
75             ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
76                 err_inval!(ReferencedConstant)
77             }
78             ErrorHandled::TooGeneric => err_inval!(TooGeneric),
79         }
80         .into()
81     }
82 }
83
84 impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
85     fn from(kind: InterpError<'tcx>) -> Self {
86         let capture_backtrace = tls::with_opt(|tcx| {
87             if let Some(tcx) = tcx {
88                 *Lock::borrow(&tcx.sess.ctfe_backtrace)
89             } else {
90                 CtfeBacktrace::Disabled
91             }
92         });
93
94         let backtrace = match capture_backtrace {
95             CtfeBacktrace::Disabled => None,
96             CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
97             CtfeBacktrace::Immediate => {
98                 // Print it now.
99                 let backtrace = Backtrace::force_capture();
100                 print_backtrace(&backtrace);
101                 None
102             }
103         };
104
105         InterpErrorInfo { kind, backtrace }
106     }
107 }
108
109 /// Error information for when the program we executed turned out not to actually be a valid
110 /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
111 /// where we work on generic code or execution does not have all information available.
112 pub enum InvalidProgramInfo<'tcx> {
113     /// Resolution can fail if we are in a too generic context.
114     TooGeneric,
115     /// Cannot compute this constant because it depends on another one
116     /// which already produced an error.
117     ReferencedConstant,
118     /// Abort in case type errors are reached.
119     TypeckError(ErrorReported),
120     /// An error occurred during layout computation.
121     Layout(layout::LayoutError<'tcx>),
122     /// An invalid transmute happened.
123     TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
124 }
125
126 impl fmt::Display for InvalidProgramInfo<'_> {
127     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128         use InvalidProgramInfo::*;
129         match self {
130             TooGeneric => write!(f, "encountered overly generic constant"),
131             ReferencedConstant => write!(f, "referenced constant has errors"),
132             TypeckError(ErrorReported) => {
133                 write!(f, "encountered constants with type errors, stopping evaluation")
134             }
135             Layout(ref err) => write!(f, "{}", err),
136             TransmuteSizeDiff(from_ty, to_ty) => write!(
137                 f,
138                 "transmuting `{}` to `{}` is not possible, because these types do not have the same size",
139                 from_ty, to_ty
140             ),
141         }
142     }
143 }
144
145 /// Details of why a pointer had to be in-bounds.
146 #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
147 pub enum CheckInAllocMsg {
148     MemoryAccessTest,
149     NullPointerTest,
150     PointerArithmeticTest,
151     InboundsTest,
152 }
153
154 impl fmt::Display for CheckInAllocMsg {
155     /// When this is printed as an error the context looks like this
156     /// "{test name} failed: pointer must be in-bounds at offset..."
157     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158         write!(
159             f,
160             "{}",
161             match *self {
162                 CheckInAllocMsg::MemoryAccessTest => "memory access",
163                 CheckInAllocMsg::NullPointerTest => "NULL pointer test",
164                 CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic",
165                 CheckInAllocMsg::InboundsTest => "inbounds test",
166             }
167         )
168     }
169 }
170
171 /// Details of an access to uninitialized bytes where it is not allowed.
172 #[derive(Debug)]
173 pub struct UninitBytesAccess {
174     /// Location of the original memory access.
175     pub access_ptr: Pointer,
176     /// Size of the original memory access.
177     pub access_size: Size,
178     /// Location of the first uninitialized byte that was accessed.
179     pub uninit_ptr: Pointer,
180     /// Number of consecutive uninitialized bytes that were accessed.
181     pub uninit_size: Size,
182 }
183
184 /// Error information for when the program caused Undefined Behavior.
185 pub enum UndefinedBehaviorInfo<'tcx> {
186     /// Free-form case. Only for errors that are never caught!
187     Ub(String),
188     /// Unreachable code was executed.
189     Unreachable,
190     /// A slice/array index projection went out-of-bounds.
191     BoundsCheckFailed {
192         len: u64,
193         index: u64,
194     },
195     /// Something was divided by 0 (x / 0).
196     DivisionByZero,
197     /// Something was "remainded" by 0 (x % 0).
198     RemainderByZero,
199     /// Overflowing inbounds pointer arithmetic.
200     PointerArithOverflow,
201     /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
202     InvalidMeta(&'static str),
203     /// Invalid drop function in vtable.
204     InvalidDropFn(FnSig<'tcx>),
205     /// Reading a C string that does not end within its allocation.
206     UnterminatedCString(Pointer),
207     /// Dereferencing a dangling pointer after it got freed.
208     PointerUseAfterFree(AllocId),
209     /// Used a pointer outside the bounds it is valid for.
210     PointerOutOfBounds {
211         ptr: Pointer,
212         msg: CheckInAllocMsg,
213         allocation_size: Size,
214     },
215     /// Using an integer as a pointer in the wrong way.
216     DanglingIntPointer(u64, CheckInAllocMsg),
217     /// Used a pointer with bad alignment.
218     AlignmentCheckFailed {
219         required: Align,
220         has: Align,
221     },
222     /// Writing to read-only memory.
223     WriteToReadOnly(AllocId),
224     // Trying to access the data behind a function pointer.
225     DerefFunctionPointer(AllocId),
226     /// The value validity check found a problem.
227     /// Should only be thrown by `validity.rs` and always point out which part of the value
228     /// is the problem.
229     ValidationFailure(String),
230     /// Using a non-boolean `u8` as bool.
231     InvalidBool(u8),
232     /// Using a non-character `u32` as character.
233     InvalidChar(u32),
234     /// The tag of an enum does not encode an actual discriminant.
235     InvalidTag(Scalar),
236     /// Using a pointer-not-to-a-function as function pointer.
237     InvalidFunctionPointer(Pointer),
238     /// Using a string that is not valid UTF-8,
239     InvalidStr(std::str::Utf8Error),
240     /// Using uninitialized data where it is not allowed.
241     InvalidUninitBytes(Option<Box<UninitBytesAccess>>),
242     /// Working with a local that is not currently live.
243     DeadLocal,
244     /// Data size is not equal to target size.
245     ScalarSizeMismatch {
246         target_size: u64,
247         data_size: u64,
248     },
249 }
250
251 impl fmt::Display for UndefinedBehaviorInfo<'_> {
252     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253         use UndefinedBehaviorInfo::*;
254         match self {
255             Ub(msg) => write!(f, "{}", msg),
256             Unreachable => write!(f, "entering unreachable code"),
257             BoundsCheckFailed { ref len, ref index } => {
258                 write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index)
259             }
260             DivisionByZero => write!(f, "dividing by zero"),
261             RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
262             PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
263             InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
264             InvalidDropFn(sig) => write!(
265                 f,
266                 "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
267                 sig
268             ),
269             UnterminatedCString(p) => write!(
270                 f,
271                 "reading a null-terminated string starting at {} with no null found before end of allocation",
272                 p,
273             ),
274             PointerUseAfterFree(a) => {
275                 write!(f, "pointer to {} was dereferenced after this allocation got freed", a)
276             }
277             PointerOutOfBounds { ptr, msg, allocation_size } => write!(
278                 f,
279                 "{} failed: pointer must be in-bounds at offset {}, \
280                            but is outside bounds of {} which has size {}",
281                 msg,
282                 ptr.offset.bytes(),
283                 ptr.alloc_id,
284                 allocation_size.bytes()
285             ),
286             DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => {
287                 write!(f, "NULL pointer is not allowed for this operation")
288             }
289             DanglingIntPointer(i, msg) => {
290                 write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i)
291             }
292             AlignmentCheckFailed { required, has } => write!(
293                 f,
294                 "accessing memory with alignment {}, but alignment {} is required",
295                 has.bytes(),
296                 required.bytes()
297             ),
298             WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a),
299             DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a),
300             ValidationFailure(ref err) => write!(f, "type validation failed: {}", err),
301             InvalidBool(b) => {
302                 write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b)
303             }
304             InvalidChar(c) => {
305                 write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c)
306             }
307             InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val),
308             InvalidFunctionPointer(p) => {
309                 write!(f, "using {} as function pointer but it does not point to a function", p)
310             }
311             InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err),
312             InvalidUninitBytes(Some(access)) => write!(
313                 f,
314                 "reading {} byte{} of memory starting at {}, \
315                  but {} byte{} {} uninitialized starting at {}, \
316                  and this operation requires initialized memory",
317                 access.access_size.bytes(),
318                 pluralize!(access.access_size.bytes()),
319                 access.access_ptr,
320                 access.uninit_size.bytes(),
321                 pluralize!(access.uninit_size.bytes()),
322                 if access.uninit_size.bytes() != 1 { "are" } else { "is" },
323                 access.uninit_ptr,
324             ),
325             InvalidUninitBytes(None) => write!(
326                 f,
327                 "using uninitialized data, but this operation requires initialized memory"
328             ),
329             DeadLocal => write!(f, "accessing a dead local variable"),
330             ScalarSizeMismatch { target_size, data_size } => write!(
331                 f,
332                 "scalar size mismatch: expected {} bytes but got {} bytes instead",
333                 target_size, data_size
334             ),
335         }
336     }
337 }
338
339 /// Error information for when the program did something that might (or might not) be correct
340 /// to do according to the Rust spec, but due to limitations in the interpreter, the
341 /// operation could not be carried out. These limitations can differ between CTFE and the
342 /// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
343 pub enum UnsupportedOpInfo {
344     /// Free-form case. Only for errors that are never caught!
345     Unsupported(String),
346     /// Could not find MIR for a function.
347     NoMirFor(DefId),
348     /// Encountered a pointer where we needed raw bytes.
349     ReadPointerAsBytes,
350     //
351     // The variants below are only reachable from CTFE/const prop, miri will never emit them.
352     //
353     /// Encountered raw bytes where we needed a pointer.
354     ReadBytesAsPointer,
355     /// Accessing thread local statics
356     ThreadLocalStatic(DefId),
357     /// Accessing an unsupported extern static.
358     ReadExternStatic(DefId),
359 }
360
361 impl fmt::Display for UnsupportedOpInfo {
362     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363         use UnsupportedOpInfo::*;
364         match self {
365             Unsupported(ref msg) => write!(f, "{}", msg),
366             ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
367             NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
368             ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
369             ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),
370             ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did),
371         }
372     }
373 }
374
375 /// Error information for when the program exhausted the resources granted to it
376 /// by the interpreter.
377 pub enum ResourceExhaustionInfo {
378     /// The stack grew too big.
379     StackFrameLimitReached,
380     /// The program ran for too long.
381     ///
382     /// The exact limit is set by the `const_eval_limit` attribute.
383     StepLimitReached,
384 }
385
386 impl fmt::Display for ResourceExhaustionInfo {
387     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388         use ResourceExhaustionInfo::*;
389         match self {
390             StackFrameLimitReached => {
391                 write!(f, "reached the configured maximum number of stack frames")
392             }
393             StepLimitReached => {
394                 write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
395             }
396         }
397     }
398 }
399
400 /// A trait to work around not having trait object upcasting.
401 pub trait AsAny: Any {
402     fn as_any(&self) -> &dyn Any;
403 }
404 impl<T: Any> AsAny for T {
405     #[inline(always)]
406     fn as_any(&self) -> &dyn Any {
407         self
408     }
409 }
410
411 /// A trait for machine-specific errors (or other "machine stop" conditions).
412 pub trait MachineStopType: AsAny + fmt::Display + Send {}
413 impl MachineStopType for String {}
414
415 impl dyn MachineStopType {
416     #[inline(always)]
417     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
418         self.as_any().downcast_ref()
419     }
420 }
421
422 #[cfg(target_arch = "x86_64")]
423 static_assert_size!(InterpError<'_>, 40);
424
425 pub enum InterpError<'tcx> {
426     /// The program caused undefined behavior.
427     UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
428     /// The program did something the interpreter does not support (some of these *might* be UB
429     /// but the interpreter is not sure).
430     Unsupported(UnsupportedOpInfo),
431     /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...).
432     InvalidProgram(InvalidProgramInfo<'tcx>),
433     /// The program exhausted the interpreter's resources (stack/heap too big,
434     /// execution takes too long, ...).
435     ResourceExhaustion(ResourceExhaustionInfo),
436     /// Stop execution for a machine-controlled reason. This is never raised by
437     /// the core engine itself.
438     MachineStop(Box<dyn MachineStopType>),
439 }
440
441 pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
442
443 impl fmt::Display for InterpError<'_> {
444     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445         use InterpError::*;
446         match *self {
447             Unsupported(ref msg) => write!(f, "{}", msg),
448             InvalidProgram(ref msg) => write!(f, "{}", msg),
449             UndefinedBehavior(ref msg) => write!(f, "{}", msg),
450             ResourceExhaustion(ref msg) => write!(f, "{}", msg),
451             MachineStop(ref msg) => write!(f, "{}", msg),
452         }
453     }
454 }
455
456 // Forward `Debug` to `Display`, so it does not look awful.
457 impl fmt::Debug for InterpError<'_> {
458     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459         fmt::Display::fmt(self, f)
460     }
461 }
462
463 impl InterpError<'_> {
464     /// Some errors allocate to be created as they contain free-form strings.
465     /// And sometimes we want to be sure that did not happen as it is a
466     /// waste of resources.
467     pub fn allocates(&self) -> bool {
468         match self {
469             // Zero-sized boxes do not allocate.
470             InterpError::MachineStop(b) => mem::size_of_val::<dyn MachineStopType>(&**b) > 0,
471             InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
472             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_))
473             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
474             | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => {
475                 true
476             }
477             _ => false,
478         }
479     }
480 }