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